mirror of
https://github.com/home-assistant/core.git
synced 2025-08-02 12:15:08 +02:00
Auto convert xml, change out the template for jsonpath
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"domain": "rest",
|
"domain": "rest",
|
||||||
"name": "RESTful",
|
"name": "RESTful",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/rest",
|
"documentation": "https://www.home-assistant.io/integrations/rest",
|
||||||
"requirements": ["xmltodict==0.12.0"],
|
"requirements": ["jsonpath==0.82", "xmltodict==0.12.0"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": []
|
"codeowners": []
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
"""Support for RESTful API sensors."""
|
"""Support for RESTful API sensors."""
|
||||||
import ast
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
|
from jsonpath import jsonpath
|
||||||
import requests
|
import requests
|
||||||
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
|
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@@ -41,11 +41,9 @@ DEFAULT_VERIFY_SSL = True
|
|||||||
DEFAULT_FORCE_UPDATE = False
|
DEFAULT_FORCE_UPDATE = False
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
CONF_CONVERT_XML = "convert_xml"
|
|
||||||
DEFAULT_CONVERT_XML = False
|
|
||||||
|
|
||||||
CONF_JSON_ATTRS = "json_attributes"
|
CONF_JSON_ATTRS = "json_attributes"
|
||||||
CONF_JSON_ATTRS_TEMPLATE = "json_attributes_template"
|
CONF_JSON_ATTRS_PATH = "json_attributes_path"
|
||||||
METHODS = ["POST", "GET"]
|
METHODS = ["POST", "GET"]
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
@@ -57,7 +55,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
),
|
),
|
||||||
vol.Optional(CONF_HEADERS): vol.Schema({cv.string: cv.string}),
|
vol.Optional(CONF_HEADERS): vol.Schema({cv.string: cv.string}),
|
||||||
vol.Optional(CONF_JSON_ATTRS, default=[]): cv.ensure_list_csv,
|
vol.Optional(CONF_JSON_ATTRS, default=[]): cv.ensure_list_csv,
|
||||||
vol.Optional(CONF_CONVERT_XML, default=DEFAULT_CONVERT_XML): cv.boolean,
|
|
||||||
vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(METHODS),
|
vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(METHODS),
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_PASSWORD): cv.string,
|
vol.Optional(CONF_PASSWORD): cv.string,
|
||||||
@@ -65,7 +62,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
||||||
vol.Optional(CONF_USERNAME): cv.string,
|
vol.Optional(CONF_USERNAME): cv.string,
|
||||||
vol.Optional(CONF_JSON_ATTRS_TEMPLATE): cv.template,
|
vol.Optional(CONF_JSON_ATTRS_PATH): cv.string,
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
|
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
|
||||||
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
||||||
@@ -93,14 +90,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
device_class = config.get(CONF_DEVICE_CLASS)
|
device_class = config.get(CONF_DEVICE_CLASS)
|
||||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||||
json_attrs = config.get(CONF_JSON_ATTRS)
|
json_attrs = config.get(CONF_JSON_ATTRS)
|
||||||
convert_xml = config.get(CONF_CONVERT_XML)
|
json_attrs_path = config.get(CONF_JSON_ATTRS_PATH)
|
||||||
json_attrs_template = config.get(CONF_JSON_ATTRS_TEMPLATE)
|
|
||||||
force_update = config.get(CONF_FORCE_UPDATE)
|
force_update = config.get(CONF_FORCE_UPDATE)
|
||||||
timeout = config.get(CONF_TIMEOUT)
|
timeout = config.get(CONF_TIMEOUT)
|
||||||
|
|
||||||
if json_attrs_template is not None:
|
|
||||||
json_attrs_template.hass = hass
|
|
||||||
|
|
||||||
if value_template is not None:
|
if value_template is not None:
|
||||||
value_template.hass = hass
|
value_template.hass = hass
|
||||||
|
|
||||||
@@ -134,8 +127,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
json_attrs,
|
json_attrs,
|
||||||
force_update,
|
force_update,
|
||||||
resource_template,
|
resource_template,
|
||||||
convert_xml,
|
json_attrs_path,
|
||||||
json_attrs_template,
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
True,
|
True,
|
||||||
@@ -156,8 +148,7 @@ class RestSensor(Entity):
|
|||||||
json_attrs,
|
json_attrs,
|
||||||
force_update,
|
force_update,
|
||||||
resource_template,
|
resource_template,
|
||||||
convert_xml,
|
json_attrs_path,
|
||||||
json_attrs_template,
|
|
||||||
):
|
):
|
||||||
"""Initialize the REST sensor."""
|
"""Initialize the REST sensor."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
@@ -171,8 +162,7 @@ class RestSensor(Entity):
|
|||||||
self._attributes = None
|
self._attributes = None
|
||||||
self._force_update = force_update
|
self._force_update = force_update
|
||||||
self._resource_template = resource_template
|
self._resource_template = resource_template
|
||||||
self._convert_xml = convert_xml
|
self._json_attrs_path = json_attrs_path
|
||||||
self._json_attrs_template = json_attrs_template
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -211,9 +201,9 @@ class RestSensor(Entity):
|
|||||||
|
|
||||||
self.rest.update()
|
self.rest.update()
|
||||||
value = self.rest.data
|
value = self.rest.data
|
||||||
json_dict = None
|
content_type = self.rest.headers.get("content-type")
|
||||||
|
|
||||||
if self._convert_xml:
|
if content_type and content_type.startswith("text/xml"):
|
||||||
try:
|
try:
|
||||||
value = json.dumps(xmltodict.parse(value))
|
value = json.dumps(xmltodict.parse(value))
|
||||||
except ExpatError:
|
except ExpatError:
|
||||||
@@ -227,14 +217,8 @@ class RestSensor(Entity):
|
|||||||
if value:
|
if value:
|
||||||
try:
|
try:
|
||||||
json_dict = json.loads(value)
|
json_dict = json.loads(value)
|
||||||
if self._json_attrs_template is not None:
|
if self._json_attrs_path is not None:
|
||||||
# render_with_possible_json_value returns single quoted
|
json_dict = jsonpath(json_dict, self._json_attrs_path)
|
||||||
# strings so we cannot use json.loads to read it back here
|
|
||||||
json_dict = ast.literal_eval(
|
|
||||||
self._json_attrs_template.render_with_possible_json_value(
|
|
||||||
value, None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if isinstance(json_dict, list):
|
if isinstance(json_dict, list):
|
||||||
json_dict = json_dict[0]
|
json_dict = json_dict[0]
|
||||||
if isinstance(json_dict, dict):
|
if isinstance(json_dict, dict):
|
||||||
@@ -278,6 +262,7 @@ class RestData:
|
|||||||
self._verify_ssl = verify_ssl
|
self._verify_ssl = verify_ssl
|
||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
self.data = None
|
self.data = None
|
||||||
|
self.headers = None
|
||||||
|
|
||||||
def set_url(self, url):
|
def set_url(self, url):
|
||||||
"""Set url."""
|
"""Set url."""
|
||||||
@@ -297,6 +282,8 @@ class RestData:
|
|||||||
verify=self._verify_ssl,
|
verify=self._verify_ssl,
|
||||||
)
|
)
|
||||||
self.data = response.text
|
self.data = response.text
|
||||||
|
self.headers = response.headers
|
||||||
except requests.exceptions.RequestException as ex:
|
except requests.exceptions.RequestException as ex:
|
||||||
_LOGGER.error("Error fetching data: %s failed with %s", self._resource, ex)
|
_LOGGER.error("Error fetching data: %s failed with %s", self._resource, ex)
|
||||||
self.data = None
|
self.data = None
|
||||||
|
self.headers = None
|
||||||
|
@@ -742,6 +742,7 @@ iperf3==0.1.11
|
|||||||
# homeassistant.components.route53
|
# homeassistant.components.route53
|
||||||
ipify==1.0.0
|
ipify==1.0.0
|
||||||
|
|
||||||
|
# homeassistant.components.rest
|
||||||
# homeassistant.components.verisure
|
# homeassistant.components.verisure
|
||||||
jsonpath==0.82
|
jsonpath==0.82
|
||||||
|
|
||||||
|
@@ -278,6 +278,7 @@ iaqualink==0.3.1
|
|||||||
# homeassistant.components.influxdb
|
# homeassistant.components.influxdb
|
||||||
influxdb==5.2.3
|
influxdb==5.2.3
|
||||||
|
|
||||||
|
# homeassistant.components.rest
|
||||||
# homeassistant.components.verisure
|
# homeassistant.components.verisure
|
||||||
jsonpath==0.82
|
jsonpath==0.82
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import pytest
|
|||||||
from pytest import raises
|
from pytest import raises
|
||||||
import requests
|
import requests
|
||||||
from requests.exceptions import RequestException, Timeout
|
from requests.exceptions import RequestException, Timeout
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
import requests_mock
|
import requests_mock
|
||||||
|
|
||||||
import homeassistant.components.rest.sensor as rest
|
import homeassistant.components.rest.sensor as rest
|
||||||
@@ -184,7 +185,6 @@ class TestRestSensorSetup(unittest.TestCase):
|
|||||||
"unit_of_measurement": DATA_MEGABYTES,
|
"unit_of_measurement": DATA_MEGABYTES,
|
||||||
"verify_ssl": "true",
|
"verify_ssl": "true",
|
||||||
"timeout": 30,
|
"timeout": 30,
|
||||||
"convert_xml": True,
|
|
||||||
"authentication": "basic",
|
"authentication": "basic",
|
||||||
"username": "my username",
|
"username": "my username",
|
||||||
"password": "my password",
|
"password": "my password",
|
||||||
@@ -206,19 +206,18 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect(
|
side_effect=self.update_side_effect(
|
||||||
'{ "key": "' + self.initial_state + '" }'
|
'{ "key": "' + self.initial_state + '" }',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.name = "foo"
|
self.name = "foo"
|
||||||
self.unit_of_measurement = DATA_MEGABYTES
|
self.unit_of_measurement = DATA_MEGABYTES
|
||||||
self.device_class = None
|
self.device_class = None
|
||||||
self.value_template = template("{{ value_json.key }}")
|
self.value_template = template("{{ value_json.key }}")
|
||||||
self.json_attrs_template = template("{{ value_json }}")
|
self.json_attrs_path = None
|
||||||
self.value_template.hass = self.hass
|
self.value_template.hass = self.hass
|
||||||
self.json_attrs_template.hass = self.hass
|
|
||||||
self.force_update = False
|
self.force_update = False
|
||||||
self.resource_template = None
|
self.resource_template = None
|
||||||
self.convert_xml = False
|
|
||||||
|
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -230,17 +229,17 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
[],
|
[],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop everything that was started."""
|
"""Stop everything that was started."""
|
||||||
self.hass.stop()
|
self.hass.stop()
|
||||||
|
|
||||||
def update_side_effect(self, data):
|
def update_side_effect(self, data, headers):
|
||||||
"""Side effect function for mocking RestData.update()."""
|
"""Side effect function for mocking RestData.update()."""
|
||||||
self.rest.data = data
|
self.rest.data = data
|
||||||
|
self.rest.headers = headers
|
||||||
|
|
||||||
def test_name(self):
|
def test_name(self):
|
||||||
"""Test the name."""
|
"""Test the name."""
|
||||||
@@ -262,7 +261,8 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
def test_update_when_value_is_none(self):
|
def test_update_when_value_is_none(self):
|
||||||
"""Test state gets updated to unknown when sensor returns no data."""
|
"""Test state gets updated to unknown when sensor returns no data."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update", side_effect=self.update_side_effect(None)
|
"rest.RestData.update",
|
||||||
|
side_effect=self.update_side_effect(None, CaseInsensitiveDict()),
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert self.sensor.state is None
|
assert self.sensor.state is None
|
||||||
@@ -272,7 +272,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test state gets updated when sensor returns a new status."""
|
"""Test state gets updated when sensor returns a new status."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect('{ "key": "updated_state" }'),
|
side_effect=self.update_side_effect(
|
||||||
|
'{ "key": "updated_state" }',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert "updated_state" == self.sensor.state
|
assert "updated_state" == self.sensor.state
|
||||||
@@ -281,7 +284,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
def test_update_with_no_template(self):
|
def test_update_with_no_template(self):
|
||||||
"""Test update when there is no value template."""
|
"""Test update when there is no value template."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update", side_effect=self.update_side_effect("plain_state")
|
"rest.RestData.update",
|
||||||
|
side_effect=self.update_side_effect(
|
||||||
|
"plain_state", CaseInsensitiveDict({"Content-Type": "application/json"})
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -293,8 +299,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
[],
|
[],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert "plain_state" == self.sensor.state
|
assert "plain_state" == self.sensor.state
|
||||||
@@ -304,7 +309,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test attributes get extracted from a JSON result."""
|
"""Test attributes get extracted from a JSON result."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect('{ "key": "some_json_value" }'),
|
side_effect=self.update_side_effect(
|
||||||
|
'{ "key": "some_json_value" }',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -316,8 +324,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert "some_json_value" == self.sensor.device_state_attributes["key"]
|
assert "some_json_value" == self.sensor.device_state_attributes["key"]
|
||||||
@@ -326,7 +333,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test attributes get extracted from a JSON list[0] result."""
|
"""Test attributes get extracted from a JSON list[0] result."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect('[{ "key": "another_value" }]'),
|
side_effect=self.update_side_effect(
|
||||||
|
'[{ "key": "another_value" }]',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -338,8 +348,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert "another_value" == self.sensor.device_state_attributes["key"]
|
assert "another_value" == self.sensor.device_state_attributes["key"]
|
||||||
@@ -348,7 +357,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
def test_update_with_json_attrs_no_data(self, mock_logger):
|
def test_update_with_json_attrs_no_data(self, mock_logger):
|
||||||
"""Test attributes when no JSON result fetched."""
|
"""Test attributes when no JSON result fetched."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update", side_effect=self.update_side_effect(None)
|
"rest.RestData.update",
|
||||||
|
side_effect=self.update_side_effect(
|
||||||
|
None, CaseInsensitiveDict({"Content-Type": "application/json"})
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -360,8 +372,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert {} == self.sensor.device_state_attributes
|
assert {} == self.sensor.device_state_attributes
|
||||||
@@ -372,7 +383,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test attributes get extracted from a JSON result."""
|
"""Test attributes get extracted from a JSON result."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect('["list", "of", "things"]'),
|
side_effect=self.update_side_effect(
|
||||||
|
'["list", "of", "things"]',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -384,8 +398,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert {} == self.sensor.device_state_attributes
|
assert {} == self.sensor.device_state_attributes
|
||||||
@@ -396,7 +409,10 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test attributes get extracted from a JSON result."""
|
"""Test attributes get extracted from a JSON result."""
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect("This is text rather than JSON data."),
|
side_effect=self.update_side_effect(
|
||||||
|
"This is text rather than JSON data.",
|
||||||
|
CaseInsensitiveDict({"Content-Type": "text/plain"}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -408,8 +424,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
assert {} == self.sensor.device_state_attributes
|
assert {} == self.sensor.device_state_attributes
|
||||||
@@ -421,7 +436,8 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect(
|
side_effect=self.update_side_effect(
|
||||||
'{ "key": "json_state_updated_value" }'
|
'{ "key": "json_state_updated_value" }',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
@@ -434,8 +450,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
|
|
||||||
@@ -444,17 +459,17 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"json_state_updated_value" == self.sensor.device_state_attributes["key"]
|
"json_state_updated_value" == self.sensor.device_state_attributes["key"]
|
||||||
), self.force_update
|
), self.force_update
|
||||||
|
|
||||||
def test_update_with_json_attrs_with_json_attrs_template(self):
|
def test_update_with_json_attrs_with_json_attrs_path(self):
|
||||||
"""Test attributes get extracted from a JSON result with a template for the attributes."""
|
"""Test attributes get extracted from a JSON result with a template for the attributes."""
|
||||||
json_attrs_template = template("{{ value_json.toplevel.second_level }}")
|
json_attrs_path = "$.toplevel.second_level"
|
||||||
json_attrs_template.hass = self.hass
|
|
||||||
value_template = template("{{ value_json.toplevel.master_value }}")
|
value_template = template("{{ value_json.toplevel.master_value }}")
|
||||||
value_template.hass = self.hass
|
value_template.hass = self.hass
|
||||||
|
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect(
|
side_effect=self.update_side_effect(
|
||||||
'{ "toplevel": {"master_value": "master", "second_level": {"some_json_key": "some_json_value", "some_json_key2": "some_json_value2" } } }'
|
'{ "toplevel": {"master_value": "master", "second_level": {"some_json_key": "some_json_value", "some_json_key2": "some_json_value2" } } }',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "application/json"}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
@@ -467,8 +482,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["some_json_key", "some_json_key2"],
|
["some_json_key", "some_json_key2"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
self.convert_xml,
|
json_attrs_path,
|
||||||
json_attrs_template,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
@@ -478,18 +492,17 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
assert "master" == self.sensor.state
|
assert "master" == self.sensor.state
|
||||||
|
|
||||||
def test_update_with_xml_convert_json_attrs_with_json_attrs_template(self):
|
def test_update_with_xml_convert_json_attrs_with_json_attrs_path(self):
|
||||||
"""Test attributes get extracted from a JSON result that was converted from XML with a template for the attributes."""
|
"""Test attributes get extracted from a JSON result that was converted from XML with a template for the attributes."""
|
||||||
json_attrs_template = template("{{ value_json.toplevel.second_level }}")
|
json_attrs_path = "$.toplevel.second_level"
|
||||||
json_attrs_template.hass = self.hass
|
|
||||||
value_template = template("{{ value_json.toplevel.master_value }}")
|
value_template = template("{{ value_json.toplevel.master_value }}")
|
||||||
value_template.hass = self.hass
|
value_template.hass = self.hass
|
||||||
convert_xml = True
|
|
||||||
|
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect(
|
side_effect=self.update_side_effect(
|
||||||
"<toplevel><master_value>master</master_value><second_level><some_json_key>some_json_value</some_json_key><some_json_key2>some_json_value2</some_json_key2></second_level></toplevel>"
|
"<toplevel><master_value>master</master_value><second_level><some_json_key>some_json_value</some_json_key><some_json_key2>some_json_value2</some_json_key2></second_level></toplevel>",
|
||||||
|
CaseInsensitiveDict({"Content-Type": "text/xml+svg"}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
@@ -502,8 +515,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["some_json_key", "some_json_key2"],
|
["some_json_key", "some_json_key2"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
convert_xml,
|
json_attrs_path,
|
||||||
json_attrs_template,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
@@ -515,16 +527,15 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
|
|
||||||
def test_update_with_xml_convert_json_attrs_with_jsonattr_template(self):
|
def test_update_with_xml_convert_json_attrs_with_jsonattr_template(self):
|
||||||
"""Test attributes get extracted from a JSON result that was converted from XML."""
|
"""Test attributes get extracted from a JSON result that was converted from XML."""
|
||||||
json_attrs_template = template("{{ value_json.response }}")
|
json_attrs_path = "$.response"
|
||||||
json_attrs_template.hass = self.hass
|
|
||||||
value_template = template("{{ value_json.response.bss.wlan }}")
|
value_template = template("{{ value_json.response.bss.wlan }}")
|
||||||
value_template.hass = self.hass
|
value_template.hass = self.hass
|
||||||
convert_xml = True
|
|
||||||
|
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect(
|
side_effect=self.update_side_effect(
|
||||||
'<?xml version="1.0" encoding="utf-8"?><response><scan>0</scan><ver>12556</ver><count>48</count><ssid>alexander</ssid><bss><valid>0</valid><name>0</name><privacy>0</privacy><wlan>bogus</wlan><strength>0</strength></bss><led0>0</led0><led1>0</led1><led2>0</led2><led3>0</led3><led4>0</led4><led5>0</led5><led6>0</led6><led7>0</led7><btn0>up</btn0><btn1>up</btn1><btn2>up</btn2><btn3>up</btn3><pot0>0</pot0><usr0>0</usr0><temp0>0x0XF0x0XF</temp0><time0> 0</time0></response>'
|
'<?xml version="1.0" encoding="utf-8"?><response><scan>0</scan><ver>12556</ver><count>48</count><ssid>alexander</ssid><bss><valid>0</valid><name>0</name><privacy>0</privacy><wlan>bogus</wlan><strength>0</strength></bss><led0>0</led0><led1>0</led1><led2>0</led2><led3>0</led3><led4>0</led4><led5>0</led5><led6>0</led6><led7>0</led7><btn0>up</btn0><btn1>up</btn1><btn2>up</btn2><btn3>up</btn3><pot0>0</pot0><usr0>0</usr0><temp0>0x0XF0x0XF</temp0><time0> 0</time0></response>',
|
||||||
|
CaseInsensitiveDict({"Content-Type": "text/xml"}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
@@ -537,8 +548,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["led0", "led1", "temp0", "time0", "ver"],
|
["led0", "led1", "temp0", "time0", "ver"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
convert_xml,
|
json_attrs_path,
|
||||||
json_attrs_template,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
@@ -554,11 +564,12 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
"""Test attributes get extracted from a XML result with bad xml."""
|
"""Test attributes get extracted from a XML result with bad xml."""
|
||||||
value_template = template("{{ value_json.toplevel.master_value }}")
|
value_template = template("{{ value_json.toplevel.master_value }}")
|
||||||
value_template.hass = self.hass
|
value_template.hass = self.hass
|
||||||
convert_xml = True
|
|
||||||
|
|
||||||
self.rest.update = Mock(
|
self.rest.update = Mock(
|
||||||
"rest.RestData.update",
|
"rest.RestData.update",
|
||||||
side_effect=self.update_side_effect("this is not xml"),
|
side_effect=self.update_side_effect(
|
||||||
|
"this is not xml", CaseInsensitiveDict({"Content-Type": "text/xml"})
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.sensor = rest.RestSensor(
|
self.sensor = rest.RestSensor(
|
||||||
self.hass,
|
self.hass,
|
||||||
@@ -570,8 +581,7 @@ class TestRestSensor(unittest.TestCase):
|
|||||||
["key"],
|
["key"],
|
||||||
self.force_update,
|
self.force_update,
|
||||||
self.resource_template,
|
self.resource_template,
|
||||||
convert_xml,
|
self.json_attrs_path,
|
||||||
self.json_attrs_template,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.sensor.update()
|
self.sensor.update()
|
||||||
|
Reference in New Issue
Block a user