Help to config RESTful API with "Complex" XML

Hi,

Overral goal

I’m trying to create a sensor that shows the status of a particular device from my router. Basically, I’m trying to simply know when my iPhone is connected to the router or not. Later on, I’ll configures things based on this status (which can be either online, offline or null)

Solution suggested

My router exposes a REST api, hence I would like to create a sensor that calls this api and get the status.

This API is getting the result as an XML :

<rsp stat="ok" version="1.0">
<host type="pc" name="RaspNAS" ip="192.168.1.152" mac="55:44:33:22:11" iface="lan4" probe="61" alive="2325652" status="online"/>
<host type="pc" name="mt7687" ip="192.168.1.113" mac="22:44:33:55:11" iface="wl1" probe="84" alive="2324476" status="online"/>
<host type="pc" name="iPhone" ip="192.168.1.139" mac="11:22:33:44:55" iface="wl0" probe="185" alive="2071344" status="online"/>
</rsp>

What I’ve tryied so far

Reading the available docs such as :

From what I understood, the XML is going to be converted as a JSON like this (based on Glitch provided in the docs) :

{
    "converted": {
        "rsp": {
            "@stat": "ok",
            "@version": "1.0",
            "host": [
                {
                    "@alive": "2325652",
                    "@iface": "lan4",
                    "@ip": "192.168.1.152",
                    "@mac": "55:44:33:22:11",
                    "@name": "RaspNAS",
                    "@probe": "61",
                    "@status": "online",
                    "@type": "pc"
                },
                {
                    "@alive": "2324476",
                    "@iface": "wl1",
                    "@ip": "192.168.1.113",
                    "@mac": "22:44:33:55:11",
                    "@name": "mt7687",
                    "@probe": "84",
                    "@status": "online",
                    "@type": "pc"
                },
                {
                    "@alive": "2071344",
                    "@iface": "wl0",
                    "@ip": "192.168.1.139",
                    "@mac": "11:22:33:44:55",
                    "@name": "iPhone",
                    "@probe": "185",
                    "@status": "online",
                    "@type": "pc"
                }
            ]
        }
    },
    "extracted": null
}

I don’t want all status but one in particular based on mac address, hence JSON Path would be :

$..host[?(@['@mac']=='11:22:33:44:55')]

Which gives this as a end result :

[
  {
    "@alive": "2071344",
    "@iface": "wl0",
    "@ip": "192.168.1.139",
    "@mac": "11:22:33:44:55",
    "@name": "TIZEN",
    "@probe": "185",
    "@status": "online",
    "@type": "pc"
  }
]

So in configuration.yaml, I’ve set it up like this :

sensor:
   - platform: rest
     scan_interval: 10
     name: iPhone WIFI Status
     resource: "http://192.168.1.1/api/?method=lan.getHostsList"
     headers:
      content_type: "text/xml"
     json_attributes_path: "$..host[?(@['@mac']=='11:22:33:44:55')]"
     value_template: "{{ value_json | map(attribute='@status') | join (',') }}"

Problem

I’m not able to get the status value (sensor is created but no value)

In the logs

Logger: homeassistant.helpers.template
Source: helpers/template.py:1822
First occurred: 2:36:41 PM (237 occurrences)
Last logged: 3:16:01 PM

Template variable warning: 'str object' has no attribute '@status' when rendering '{{ value_json | map(attribute='@status') | join (',') }}'

I don’t know why it does not work, as when i’m testing in the devs tools / template it works:

{% set my_test_json = [
  {
    "@alive": "2071344",
    "@iface": "wl0",
    "@ip": "192.168.1.139",
    "@mac": "11:22:33:44:55",
    "@name": "TIZEN",
    "@probe": "185",
    "@status": "online",
    "@type": "pc"
  }
]
%}

The status is {{ my_test_json | map(attribute='@status') | join (',') }}.
The status is online.

Can you help me on this ? What am I doing wrong.

Note: the API is working as expected, I’m able to reach it from any web browser and PostMan. There is no need of authentification. I’ve try as well to replace “192.168.1.1” by my actual IP address but still in does not work.

Note2: I’ve tryied to play arround the synthax (like removing the quotes in the ressources etc…). I’ve try to defined “json_attributes” as well.

value_json represents the received payload from the REST call. I get the impression you think it receives the result of json_attributes_path (which is used for populating the sensor’s attributes).

Thanks for your reply, yes that what I though.

Then how should I specify which value I want from the result of json_attributes_path ?

I’ve tried like this but no luck

sensor:
   - platform: rest
     scan_interval: 10
     name: iPhone WIFI Status
     resource: http://192.168.1.1/api/?method=lan.getHostsList
     headers:
      content_type: "text/xml"
     json_attributes_path: "$..host[?(@['@mac']=='11:22:33:44:55')]"
     json_attributes:
        - "@status"
     value_template: "{{ value_json | map(attribute='@status') | join (',') }}"

Got the same issue as previously :

Template variable warning: 'str object' has no attribute '@status' when rendering '{{ value_json | map(attribute='@status') | join (',') }}'

And this one as well now

Logger: homeassistant.components.rest.sensor
Source: components/rest/sensor.py:186
Integration: RESTful (documentation, issues)
First occurred: 5:27:46 PM (6 occurrences)
Last logged: 5:28:36 PM

JSON result was not a dictionary or list with 0th element a dictionary

The error message refers to the template in the value_template option.

value_json contains the complete payload you posted so attribute='@status' is insufficient to extract what you want.

Template variable warning: ‘str object’ has no attribute ‘@status

Copy-paste the following into the Template Editor and confirm it reports what you expect:

{% set value_json = 
{
    "converted": {
        "rsp": {
            "@stat": "ok",
            "@version": "1.0",
            "host": [
                {
                    "@alive": "2325652",
                    "@iface": "lan4",
                    "@ip": "192.168.1.152",
                    "@mac": "55:44:33:22:11",
                    "@name": "RaspNAS",
                    "@probe": "61",
                    "@status": "online",
                    "@type": "pc"
                },
                {
                    "@alive": "2324476",
                    "@iface": "wl1",
                    "@ip": "192.168.1.113",
                    "@mac": "22:44:33:55:11",
                    "@name": "mt7687",
                    "@probe": "84",
                    "@status": "online",
                    "@type": "pc"
                },
                {
                    "@alive": "2071344",
                    "@iface": "wl0",
                    "@ip": "192.168.1.139",
                    "@mac": "11:22:33:44:55",
                    "@name": "iPhone",
                    "@probe": "185",
                    "@status": "online",
                    "@type": "pc"
                }
            ]
        }
    },
    "extracted": null
}
%}
{{ value_json.converted.rsp.host | selectattr('@mac', 'eq', '11:22:33:44:55') | map(attribute='@status') | first  }}

If it does what you want, use the final line in value_template and see if that works as expected.

value_template: "{{ value_json.converted.rsp.host | selectattr('@mac', 'eq', '11:22:33:44:55') | map(attribute='@status') | first  }}"

Many thanks it works as expected.

Note that the tool provided in the docs (Glitch) creates the attribute “converted” which does not exists in the original xml nor in the final conversion done by Home Assistant, so I just had to remove it to make it works !

1 Like