#Custom Sensor: value_template / String Builder

1 messages · Page 1 of 1 (latest)

plush flare
#

Created a custom geolocation sensor that will take lat and long values (one sensor each) and returns the full address.

For example: While driving, house_number attribute may not be available, only road, city and so on. When one of those attributes is not available and/or 'None', the sensor value is "Unknown".

Is there a way to use a string builder like feature that will allow me to build a string based on available attributes (and or not None)?

#
sensor:
  - platform: rest
    name: geolocation.x3_xdrive20i
    icon: mdi:map-marker
    resource: https://nominatim.openstreetmap.org/reverse
    method: GET
    params:
      format: json
      layer: address
      lat: "{{ states['device_tracker.x3_xdrive20i'].attributes.latitude }}"
      lon: "{{ states['device_tracker.x3_xdrive20i'].attributes.longitude }}"
    json_attributes_path: "$.address"
    json_attributes:
        - house_number
        - road
        - neighbourhood
        - city
        - state
        - ISO3166-2-lvl4
        - country
        - postcode
        - country_code
    value_template: >
        {% if value_json.display_name != 'None' %}
            {{ value_json.address.road 
            + ' ' + value_json.address.house_number 
            + ', ' + value_json.address.postcode 
            + ', ' + value_json.address.city 
            + ' (' + value_json.address.country_code | upper + ')' }}
        {% else %}
            {{"Unknown location..."}}
        {% endif %}
plush flare
#

To explain my issue further. Have been playing inside the template editor. Test code below. This code tests if JSON item has value None, if not add it to the list ns.value_list.

{% set value_json = {
  "display_name": "whateverroadname 10",
  "address": {
    "road": "whateverroadname",
    "house_number": "10",
    "test": "None"
  }
} %}


{% if not value_json.display_name == "Unknown" %}
  {% set ns=namespace(value_list=[]) %}
  {% for key, value in value_json.address | items %}
    {% if value != 'None' %}
      {% set ns.value_list = ns.value_list ~ value %}
    {% endif %}
  {% endfor %}
  {{ ns.value_list }}
{% else %}
  {{ "Unknown location..." }}
{% endif %}

This results in: []whateverroadname10 while I am expecting whateverroadname 10.
In addition, {{ ns.value_list | join(',') }} results in: [,],w,h,a,t,e,v,e,r,r,o,a,d,n,a,m,e,1,0 instead of whateverroadname,10

According to the jinja documentation, list() is supposed to return a list of characters. However, it does not explain [] ate the beginning of my result.

My main question is:
How do I iterate through a JSON response, and build a string based on items that do not have the value None which would result in whateverroadname, 10

Many thanks in advance for any pointers or assistance!

plush flare
#

OK, removed the prefix [] in my result by changing {% set ns=namespace(value_list=[]) %} to {% set ns=namespace(value_list='') %}.

plush flare
#

My solution so far, but there must be a cleaner method. The code checks if attribute in JSON has a value (rest sensor returns None if not). Then create a new dict, containing only JSON key/value pairs that do have a value. With if/else check for specific keys and build a new string to out put. Place only a command when specified key does not exist. In geolocation that is only road, house_number and zip code. City and Country are always known (in my case).

#
sensor:
  - platform: rest
    name: geolocation.x3_xdrive20i
    icon: mdi:map-marker
    resource: https://nominatim.openstreetmap.org/reverse
    method: GET
    params:
      format: json
      layer: address
      lat: "{{ states['device_tracker.x3_xdrive20i'].attributes.latitude }}"
      lon: "{{ states['device_tracker.x3_xdrive20i'].attributes.longitude }}"
    json_attributes_path: "$.address"
    json_attributes:
        - road
        - house_number
        - postcode
        - city
        - country_code
    value_template: >
        {% set ns=namespace(value_list=[],out_string="") %}
            {% for key, value in value_json.address | items %}
                {% if value != 'None' %}
                    {% set ns.value_list = ns.value_list + [ ("%s" % (key), "%s" % (value)) ] %}
                {% endif %}
            {% endfor %}
        {% set out_dict = dict.from_keys(ns.value_list) %}
        
        {% if out_dict['road'] is defined %}
            {% set ns.out_string = ns.out_string ~ "%s" % out_dict['road'] %}
        {% endif %}
        
        {% if out_dict['house_number'] is defined %}
            {% set ns.out_string = ns.out_string ~ " %s, " % out_dict['house_number'] %}
        {% else %}
            {% set ns.out_string = ns.out_string ~ ", " %}
        {% endif %}
        
        {% if out_dict['postcode'] is defined %}
            {% set ns.out_string = ns.out_string ~ " %s, " % out_dict['postcode'] %}
        {% else %}
            {% set ns.out_string = ns.out_string ~ ", " %}
        {% endif %}
        
        {% if out_dict['city'] is defined %}
            {% set ns.out_string = ns.out_string ~ " %s" % out_dict['city'] %}
        {% endif %}
        
        {% if out_dict['country_code'] is defined %}
            {% set ns.out_string = ns.out_string ~ " (%s)" % out_dict['country_code'] | upper %}
        {% endif %}
        {{ ns.out_string }}
#

Output results in <roadname> <housenr>, <zip>, <city> (<country code in upper case>) . Worst case <city> (<country code in upper case>).