Linking attributes to match persons to AP

in HA, I want to know to which Unifi AP a person is connected. And since I don’t know up front who will be connected, this needs to be flexible.

I can find this manually via the following path:
person.xyz.attributes.source gives me the device_tracker entity, this entity contains the attribute ap_mac, this ap_mac value points to the mac attribute of the AP (device_tracker.wifi_ap_name.attributes.mac)

So after some fiddling around, I came up with the following template that basically collects all the info I need:

{% set peepatap = namespace(loc='') %}
{# Select all the person objects that have the source attribute defined #}
{% 
  set peeps = states.person | 
    selectattr('attributes.source', 'defined') |
    map(attribute='entity_id') | 
    list
%}

{# For each found person, find if they are connected to an AP #}
{# if no AP is found the person is away #}
{# Fill the list with the info that is needed #}{% for peep in peeps %}
  {% 
    set aps = states.device_tracker | 
      selectattr('attributes.mac', 'defined') | 
      selectattr('attributes.mac', 'eq', 
        state_attr(state_attr(peep, 'source'), 'ap_mac')
      ) | 
      map(attribute='name') | 
      list %}
  {% if peepatap.loc != '' %}{% set peepatap.loc = peepatap.loc + ',' %}{% endif %}
  {% if (aps | count) == 0 %}
    {% set peepatap.loc = peepatap.loc + '{ "state": "away", "friendly_name": "' + state_attr(peep,"friendly_name") + '", "person": "' + peep + '", "ap": "None" }' %}
  {% else %}
    {% for ap in aps %}
      {% set peepatap.loc = peepatap.loc + '{ "state": "home", "friendly_name": "' + state_attr(peep,"friendly_name") + '", "person": "' + peep + '", "ap": "' + ap + '" }' %}
    {% endfor %}
  {% endif %}
{% endfor %}

{# Make sure it is string containing a JSON list of dict #}
{% set peepatap.loc = '[' + peepatap.loc + ']' %}

No I can get the list from

{{ peepatap.loc }}

and it looks like this:

[
  {
    "state": "away",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "None"
  },
  {
    "state": "home",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "DC"
  },
  {
    "state": "home",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "Living"
  },
  {
    "state": "home",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "DC"
  },
  {
    "state": "away",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "None"
  },
  {
    "state": "away",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "None"
  },
  {
    "state": "away",
    "friendly_name": "***",
    "person": "person.***",
    "ap": "None"
  }
]

If I would know in advance which persons I should look for, I could use this piece of template:

{{ 
  states.device_tracker | 
    selectattr('attributes.mac', 'defined') | 
    selectattr('attributes.mac', 'eq', 
      state_attr(state_attr('person.***', 'source'), 'ap_mac')
    ) | 
    map(attribute='name') |
    list |
    first
}}

However, if the person is not connected to an AP, the list is empty and first errors out, so I could take off | first but this results in a list and I want a string.

And here are my questions:

  • Can I use this template/list to create sensors in an automation? (This is my preferred option so that I can use the auto-entities card to populat a card with the info I want)
  • Can I use this list in a Lovalace card to display the presense of people and to which AP they are connected?
  • Is there a custom card that can handle this kind of templating to create cards from the info in the list I created?

If only 1 of these questions can be answered with yes, I will be elated.

Thank you very much for your help.

Ok, I have found and implemented some kind of solution to my problem.

First I created a template:

  • in configuration.yaml:
template: !include include.d/template.yaml
  • in include.d/template.yaml:
 sensor:
  - name: "ap_all"
    state: "ALL APs"
    attributes:
        loc: >-
          {% set peepatap = namespace(loc='') %}
          {# Select all the person objects that have the source attribute defined #}
          {%
            set peeps = states.person |
              selectattr('attributes.source', 'defined') |
              map(attribute='entity_id') |
              list
           %}

           {# For each found person, find if they are connected to an AP #}
           {# if no AP is found the person is away #}
           {# Fill the list with the info that is needed #}
           {% for peep in peeps %}
             {%
               set aps = states.device_tracker |
                 selectattr('attributes.mac', 'defined') |
                 selectattr('attributes.mac', 'eq',
                   state_attr(state_attr(peep, 'source'), 'ap_mac')
                 ) |
                 map(attribute='name') |
                 list
               %}
               {% if (aps | count) != 0 %}
                 {% if peepatap.loc != '' %}{% set peepatap.loc = peepatap.loc + ',' %}{% endif %}
                 {% for ap in aps %}
                   {% set peepatap.loc = peepatap.loc + '{ "state": "home", "friendly_name": "' + state_attr(peep,"friendly_name") + '", "person": "' + peep + '", "ap": "' + ap + '" }' %}
                 {% endfor %}
               {% endif %}
             {% endfor %}

             {# Make sure it is string containing a JSON list of dict #}
             {% set peepatap.loc = '[' + peepatap.loc + ']' %}
             {{ peepatap.loc }}

This results in a sensor that looks like this:

Next I create a Markdown card as follows:

- type: markdown
            entities:
              - sensor.ap_all
            content: |-
              {% set AP_LIST = {"DC": "DC", "WK": "Woonkamer", "WN": "Wellness", "SR": "Schuur"} %}
              {% set Table_contents = namespace(rows = "") %}
              {% for item in state_attr("sensor.ap_all", "loc") %}
                {% set Table_contents.rows = Table_contents.rows + "<TR><TD>" + item.friendly_name + "</TD><TD>&nbsp;</TD><TD>" + AP_LIST[item.ap] + "</TR>" %}
              {% endfor %}
              # People connected to WiFi Access Points

              <TABLE>
                <TR><TH><ha-icon icon="mdi:account"></ha-icon></TH><TH>&nbsp;</TH><TH><ha-icon icon="mdi:wifi"></ha-icon></TH></TR>
                <TR><TH colspan=3><hr></TH></TR>
                {{ Table_contents.rows }}
              </TABLE>

which results in a card that looks like this:

So I hope someone else can benefit of my research. And please feel free to comment on my results. I’m always looking to improve what I’ve made.