Reuse of YAML

I have 2 template binary_sensors defined in YAML (in a templates file ‘included’ in configuration.yaml). They are identical apart from 1 or maybe 2 variables (DEFANGLE and CLOUDY) that I set. I would like to create the code once and then re-use that YAML with differing values for 1 or 2 of those variables.

I’ve looked at ‘includes’ and ‘anchors’, but the former seems to not be usable in this case and the latter is not clear to me.

The following works as I want, but with much duplicated code and I’m a DRY lover so would like to coalesce the basic code into one ‘block’ and re-use that in the 2 sensors, but varying only by 1 or 2 variable values.

Currently I’ve been unable to figure out a solution to this, so am hoping someone more expert in YAML can offer some guidance.

- binary_sensor:
  - name: "daylight"
    unique_id: binary_sensor.daylights
    variables:
#   adjust these to suit
#    default elevation to use
      DEFANGLE: -2
#    adjustment for cloudy conditions
      CLOUDY: +4
#   don't touch the rest
      ELEV2USE: >
        {% set WEATHER = states('weather.forecast_home') %}
        {% if WEATHER in ['sunny','clear','clear-night'] %}
          {{ DEFANGLE }}
        {% elif WEATHER == 'partlycloudy' %}
          {{ DEFANGLE + CLOUDY / 2 }}
        {% else %}
          {{ DEFANGLE + CLOUDY }}
        {% endif %}
#     get degrees as seconds @ 4 min/°
      ELEVASECS: >
        {{ ELEV2USE * 4 * 60 }}
      NEXTDARK: >
        {{ (states('sensor.sun_next_setting') | as_timestamp - ELEVASECS) | timestamp_custom("%H:%M", True) }}
      NEXTLIGHT: >
        {{ (states('sensor.sun_next_rising') | as_timestamp + ELEVASECS) | timestamp_custom("%H:%M", True) }}
    state: >
      {{ states.sun.sun.attributes.elevation > ELEV2USE }}
    icon: >
      {% if this.state == "on" %}
        mdi:brightness-5
      {% else %}
        mdi:brightness-3
      {% endif %}
    attributes:
      DEFANGLE: "{{ DEFANGLE }}"
      ELEV2USE: "{{ ELEV2USE }}"
      NEXTDARK: "{{ NEXTDARK }}"
      NEXTLIGHT: "{{ NEXTLIGHT }}"

  - name: "dayloght"
    unique_id: binary_sensor.dayloghts
    variables:
#   adjust these to suit
#    default elevation to use
      DEFANGLE: -8
#    adjustment for cloudy conditions
      CLOUDY: +4
#   don't touch the rest
      ELEV2USE: >
        {% set WEATHER = states('weather.forecast_home') %}
        {% if WEATHER in ['sunny','clear','clear-night'] %}
          {{ DEFANGLE }}
        {% elif WEATHER == 'partlycloudy' %}
          {{ DEFANGLE + CLOUDY / 2 }}
        {% else %}
          {{ DEFANGLE + CLOUDY }}
        {% endif %}
#     get degrees as seconds @ 4 min/°
      ELEVASECS: >
        {{ ELEV2USE * 4 * 60 }}
      NEXTDARK: >
        {{ (states('sensor.sun_next_setting') | as_timestamp - ELEVASECS) | timestamp_custom("%H:%M", True) }}
      NEXTLIGHT: >
        {{ (states('sensor.sun_next_rising') | as_timestamp + ELEVASECS) | timestamp_custom("%H:%M", True) }}
    state: >
      {{ states.sun.sun.attributes.elevation > ELEV2USE }}
    icon: >
      {% if this.state == "on" %}
        mdi:brightness-5
      {% else %}
        mdi:brightness-3
      {% endif %}
    attributes:
      DEFANGLE: "{{ DEFANGLE }}"
      ELEV2USE: "{{ ELEV2USE }}"
      NEXTDARK: "{{ NEXTDARK }}"
      NEXTLIGHT: "{{ NEXTLIGHT }}"

That eliminates me.

I don’t think there is a solution because when you start Home Assistant the configured sensors are created. You can’t change the sensor configuration dynamically.

Template entity blueprints.
Still on an experimental level though and may or may not work well enough yet…

I often wish HA were a little bit more friendly to DRY.

In any event, another option here is to define custom Jinja macros, but then you still end up with having to duplicate the import/call-macro code.

A more radical option is to create just one sensor, but for each desired variant thereof (DEFANGLE and CLOUDY in this instance), have a label and use it as a key in a dictionary in an attribute.

For example, in my setup, rather than having a separate sensor entity for each room in my house, I have a single sensor entity where a “temperatures” attribute is a dictionary with room_name/temperature pairs. It does make the template code a little bit more convoluted, but it can avoid a lot of repetition.

I had thought of using a single sensor, with attributes to return the lighting up times for inside and outside, but on the whole wanted the simplicity of using binary sensors.

In any case, that would involve duplicating most of the calculations to arrive at the different values, so wouldn’t actually save anything.

In theory, once I’ve got DEFANGLE and CLOUDY set to suitable values, it shouldn’t then need messing with, so being WET (I.e. not DRY :smile:) won’t present a huge maintenance problem.

OTOH, I could just use light sensors inside and outside to indicate when lights should turn on, but where’s the fun in that. :grin:

For DRY there is Lovelace_gen.