mirror of https://github.com/home-assistant/core
626 lines
19 KiB
Python
626 lines
19 KiB
Python
"""The tests for the Scrape sensor platform."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
from unittest.mock import patch
|
|
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
import pytest
|
|
|
|
from homeassistant.components.scrape.const import (
|
|
CONF_ENCODING,
|
|
CONF_INDEX,
|
|
CONF_SELECT,
|
|
DEFAULT_ENCODING,
|
|
DEFAULT_SCAN_INTERVAL,
|
|
DEFAULT_VERIFY_SSL,
|
|
)
|
|
from homeassistant.components.sensor import (
|
|
CONF_STATE_CLASS,
|
|
DOMAIN as SENSOR_DOMAIN,
|
|
SensorDeviceClass,
|
|
SensorStateClass,
|
|
)
|
|
from homeassistant.const import (
|
|
CONF_DEVICE_CLASS,
|
|
CONF_ICON,
|
|
CONF_METHOD,
|
|
CONF_NAME,
|
|
CONF_RESOURCE,
|
|
CONF_TIMEOUT,
|
|
CONF_UNIQUE_ID,
|
|
CONF_UNIT_OF_MEASUREMENT,
|
|
CONF_VALUE_TEMPLATE,
|
|
CONF_VERIFY_SSL,
|
|
STATE_UNAVAILABLE,
|
|
STATE_UNKNOWN,
|
|
UnitOfTemperature,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.helpers.trigger_template_entity import (
|
|
CONF_AVAILABILITY,
|
|
CONF_PICTURE,
|
|
)
|
|
from homeassistant.setup import async_setup_component
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from . import MockRestData, return_integration_config
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
DOMAIN = "scrape"
|
|
|
|
|
|
async def test_scrape_sensor(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor minimal."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[{"select": ".current-version h1", "name": "HA version"}]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
|
|
|
|
async def test_scrape_sensor_value_template(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with value template."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-version h1",
|
|
"name": "HA version",
|
|
"value_template": "{{ value.split(':')[1] }}",
|
|
}
|
|
]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state.state == "2021.12.10"
|
|
|
|
|
|
async def test_scrape_uom_and_classes(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor for unit of measurement, device class and state class."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-temp h3",
|
|
"name": "Current Temp",
|
|
"value_template": "{{ value.split(':')[1] }}",
|
|
"unit_of_measurement": "°C",
|
|
"device_class": "temperature",
|
|
"state_class": "measurement",
|
|
}
|
|
]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_uom_and_classes")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.current_temp")
|
|
assert state.state == "22.1"
|
|
assert state.attributes[CONF_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS
|
|
assert state.attributes[CONF_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE
|
|
assert state.attributes[CONF_STATE_CLASS] == SensorStateClass.MEASUREMENT
|
|
|
|
|
|
async def test_scrape_unique_id(
|
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
|
) -> None:
|
|
"""Test Scrape sensor for unique id."""
|
|
config = {
|
|
DOMAIN: return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-temp h3",
|
|
"name": "Current Temp",
|
|
"value_template": "{{ value.split(':')[1] }}",
|
|
"unique_id": "very_unique_id",
|
|
}
|
|
]
|
|
)
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_uom_and_classes")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.current_temp")
|
|
assert state.state == "22.1"
|
|
|
|
entry = entity_registry.async_get("sensor.current_temp")
|
|
assert entry
|
|
assert entry.unique_id == "very_unique_id"
|
|
|
|
|
|
async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with authentication."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
authentication="digest",
|
|
username="user@secret.com",
|
|
password="12345678",
|
|
sensors=[
|
|
{
|
|
"select": ".return",
|
|
"name": "Auth page",
|
|
},
|
|
],
|
|
),
|
|
return_integration_config(
|
|
username="user@secret.com",
|
|
password="12345678",
|
|
sensors=[
|
|
{
|
|
"select": ".return",
|
|
"name": "Auth page2",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor_authentication")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.auth_page")
|
|
assert state.state == "secret text"
|
|
state2 = hass.states.get("sensor.auth_page2")
|
|
assert state2.state == "secret text"
|
|
|
|
|
|
async def test_scrape_sensor_no_data(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test Scrape sensor fails on no data."""
|
|
config = {
|
|
DOMAIN: return_integration_config(
|
|
sensors=[{"select": ".current-version h1", "name": "HA version"}]
|
|
)
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor_no_data")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state is None
|
|
|
|
assert "Platform scrape not ready yet" in caplog.text
|
|
|
|
|
|
async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor no data on refresh."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[{"select": ".current-version h1", "name": "HA version"}]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
|
|
mocker.payload = "test_scrape_sensor_no_data"
|
|
async_fire_time_changed(hass, dt_util.utcnow() + DEFAULT_SCAN_INTERVAL)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state is not None
|
|
assert state.state == STATE_UNAVAILABLE
|
|
|
|
|
|
async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with attribute and tag."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"index": 1,
|
|
"select": "div",
|
|
"name": "HA class",
|
|
"attribute": "class",
|
|
},
|
|
{"select": "template", "name": "HA template"},
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_class")
|
|
assert state.state == "['links']"
|
|
state2 = hass.states.get("sensor.ha_template")
|
|
assert state2.state == "Trying to get"
|
|
|
|
|
|
async def test_scrape_sensor_device_date(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with a device of type DATE."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".release-date",
|
|
"name": "HA Date",
|
|
"device_class": "date",
|
|
"value_template": "{{ strptime(value, '%B %d, %Y').strftime('%Y-%m-%d') }}",
|
|
}
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_date")
|
|
assert state.state == "2022-01-17"
|
|
|
|
|
|
async def test_scrape_sensor_device_date_errors(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with a device of type DATE."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-version h1",
|
|
"name": "HA Date",
|
|
"device_class": "date",
|
|
}
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_date")
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
|
|
async def test_scrape_sensor_device_timestamp(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with a device of type TIMESTAMP."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".utc-time",
|
|
"name": "HA Timestamp",
|
|
"device_class": "timestamp",
|
|
}
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_timestamp")
|
|
assert state.state == "2022-12-22T13:15:30+00:00"
|
|
|
|
|
|
async def test_scrape_sensor_device_timestamp_error(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor with a device of type TIMESTAMP."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-time",
|
|
"name": "HA Timestamp",
|
|
"device_class": "timestamp",
|
|
}
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_timestamp")
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
|
|
async def test_scrape_sensor_errors(hass: HomeAssistant) -> None:
|
|
"""Test Scrape sensor handle errors."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"index": 5,
|
|
"select": "div",
|
|
"name": "HA class",
|
|
"attribute": "class",
|
|
},
|
|
{
|
|
"select": "div",
|
|
"name": "HA class2",
|
|
"attribute": "classes",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_class")
|
|
assert state.state == STATE_UNKNOWN
|
|
state2 = hass.states.get("sensor.ha_class2")
|
|
assert state2.state == STATE_UNKNOWN
|
|
|
|
|
|
async def test_scrape_sensor_unique_id(
|
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
|
) -> None:
|
|
"""Test Scrape sensor with unique_id."""
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
"select": ".current-version h1",
|
|
"name": "HA version",
|
|
"unique_id": "ha_version_unique_id",
|
|
}
|
|
]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.ha_version")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
|
|
entity = entity_registry.async_get("sensor.ha_version")
|
|
|
|
assert entity.unique_id == "ha_version_unique_id"
|
|
|
|
|
|
async def test_setup_config_entry(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
loaded_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test setup from config entry."""
|
|
|
|
state = hass.states.get("sensor.current_version")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
|
|
entity = entity_registry.async_get("sensor.current_version")
|
|
|
|
assert entity.unique_id == "3699ef88-69e6-11ed-a1eb-0242ac120002"
|
|
|
|
|
|
async def test_templates_with_yaml(hass: HomeAssistant) -> None:
|
|
"""Test the Scrape sensor from yaml config with templates."""
|
|
|
|
hass.states.async_set("sensor.input1", "on")
|
|
hass.states.async_set("sensor.input2", "on")
|
|
await hass.async_block_till_done()
|
|
|
|
config = {
|
|
DOMAIN: [
|
|
return_integration_config(
|
|
sensors=[
|
|
{
|
|
CONF_NAME: "Get values with template",
|
|
CONF_SELECT: ".current-version h1",
|
|
CONF_INDEX: 0,
|
|
CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002",
|
|
CONF_ICON: '{% if states("sensor.input1")=="on" %} mdi:on {% else %} mdi:off {% endif %}',
|
|
CONF_PICTURE: '{% if states("sensor.input1")=="on" %} /local/picture1.jpg {% else %} /local/picture2.jpg {% endif %}',
|
|
CONF_AVAILABILITY: '{{ states("sensor.input2")=="on" }}',
|
|
}
|
|
]
|
|
)
|
|
]
|
|
}
|
|
|
|
mocker = MockRestData("test_scrape_sensor")
|
|
with patch(
|
|
"homeassistant.components.rest.RestData",
|
|
return_value=mocker,
|
|
):
|
|
assert await async_setup_component(hass, DOMAIN, config)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.get_values_with_template")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
assert state.attributes[CONF_ICON] == "mdi:on"
|
|
assert state.attributes["entity_picture"] == "/local/picture1.jpg"
|
|
|
|
hass.states.async_set("sensor.input1", "off")
|
|
await hass.async_block_till_done()
|
|
|
|
async_fire_time_changed(
|
|
hass,
|
|
dt_util.utcnow() + timedelta(minutes=10),
|
|
)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.get_values_with_template")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
assert state.attributes[CONF_ICON] == "mdi:off"
|
|
assert state.attributes["entity_picture"] == "/local/picture2.jpg"
|
|
|
|
hass.states.async_set("sensor.input2", "off")
|
|
await hass.async_block_till_done()
|
|
|
|
async_fire_time_changed(
|
|
hass,
|
|
dt_util.utcnow() + timedelta(minutes=20),
|
|
)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.get_values_with_template")
|
|
assert state.state == STATE_UNAVAILABLE
|
|
|
|
hass.states.async_set("sensor.input1", "on")
|
|
hass.states.async_set("sensor.input2", "on")
|
|
await hass.async_block_till_done()
|
|
|
|
async_fire_time_changed(
|
|
hass,
|
|
dt_util.utcnow() + timedelta(minutes=30),
|
|
)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.get_values_with_template")
|
|
assert state.state == "Current Version: 2021.12.10"
|
|
assert state.attributes[CONF_ICON] == "mdi:on"
|
|
assert state.attributes["entity_picture"] == "/local/picture1.jpg"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"get_config",
|
|
[
|
|
{
|
|
CONF_RESOURCE: "https://www.home-assistant.io",
|
|
CONF_METHOD: "GET",
|
|
CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL,
|
|
CONF_TIMEOUT: 10,
|
|
CONF_ENCODING: DEFAULT_ENCODING,
|
|
SENSOR_DOMAIN: [
|
|
{
|
|
CONF_SELECT: ".current-version h1",
|
|
CONF_NAME: "Current version",
|
|
CONF_VALUE_TEMPLATE: "{{ value.split(':')[1] }}",
|
|
CONF_INDEX: 0,
|
|
CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002",
|
|
CONF_AVAILABILITY: '{{ states("sensor.input1")=="on" }}',
|
|
}
|
|
],
|
|
}
|
|
],
|
|
)
|
|
async def test_availability(
|
|
hass: HomeAssistant,
|
|
loaded_entry: MockConfigEntry,
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Test availability when setup from config entry."""
|
|
|
|
hass.states.async_set("sensor.input1", "on")
|
|
freezer.tick(timedelta(minutes=10))
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.current_version")
|
|
assert state.state == "2021.12.10"
|
|
|
|
hass.states.async_set("sensor.input1", "off")
|
|
await hass.async_block_till_done()
|
|
|
|
freezer.tick(timedelta(minutes=10))
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("sensor.current_version")
|
|
assert state.state == STATE_UNAVAILABLE
|