1 """Support for getting data from websites with scraping."""
3 from __future__
import annotations
6 from typing
import Any, cast
8 import voluptuous
as vol
18 CONF_UNIT_OF_MEASUREMENT,
29 TEMPLATE_SENSOR_BASE_SCHEMA,
31 ManualTriggerSensorEntity,
36 from .
import ScrapeConfigEntry
37 from .const
import CONF_INDEX, CONF_SELECT, DOMAIN
38 from .coordinator
import ScrapeCoordinator
40 _LOGGER = logging.getLogger(__name__)
42 TRIGGER_ENTITY_OPTIONS = (
49 CONF_UNIT_OF_MEASUREMENT,
56 async_add_entities: AddEntitiesCallback,
57 discovery_info: DiscoveryInfoType |
None =
None,
59 """Set up the Web scrape sensor."""
60 discovery_info = cast(DiscoveryInfoType, discovery_info)
61 coordinator: ScrapeCoordinator = discovery_info[
"coordinator"]
62 sensors_config: list[ConfigType] = discovery_info[
"configs"]
64 await coordinator.async_refresh()
65 if coordinator.data
is None:
66 raise PlatformNotReady
68 entities: list[ScrapeSensor] = []
69 for sensor_config
in sensors_config:
70 trigger_entity_config = {CONF_NAME: sensor_config[CONF_NAME]}
71 for key
in TRIGGER_ENTITY_OPTIONS:
72 if key
not in sensor_config:
74 trigger_entity_config[key] = sensor_config[key]
80 trigger_entity_config,
81 sensor_config[CONF_SELECT],
82 sensor_config.get(CONF_ATTRIBUTE),
83 sensor_config[CONF_INDEX],
84 sensor_config.get(CONF_VALUE_TEMPLATE),
94 entry: ScrapeConfigEntry,
95 async_add_entities: AddEntitiesCallback,
97 """Set up the Scrape sensor entry."""
100 coordinator = entry.runtime_data
101 config =
dict(entry.options)
102 for sensor
in config[
"sensor"]:
103 sensor_config: ConfigType = vol.Schema(
104 TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.ALLOW_EXTRA
107 name: str = sensor_config[CONF_NAME]
108 value_string: str |
None = sensor_config.get(CONF_VALUE_TEMPLATE)
110 value_template: Template |
None = (
111 Template(value_string, hass)
if value_string
is not None else None
114 trigger_entity_config: dict[str, str | Template |
None] = {CONF_NAME: name}
115 for key
in TRIGGER_ENTITY_OPTIONS:
116 if key
not in sensor_config:
118 if key == CONF_AVAILABILITY:
119 trigger_entity_config[key] =
Template(sensor_config[key], hass)
121 trigger_entity_config[key] = sensor_config[key]
127 trigger_entity_config,
128 sensor_config[CONF_SELECT],
129 sensor_config.get(CONF_ATTRIBUTE),
130 sensor_config[CONF_INDEX],
140 """Representation of a web scrape sensor."""
145 coordinator: ScrapeCoordinator,
146 trigger_entity_config: ConfigType,
150 value_template: Template |
None,
153 """Initialize a web scrape sensor."""
154 CoordinatorEntity.__init__(self, coordinator)
155 ManualTriggerSensorEntity.__init__(self, hass, trigger_entity_config)
161 if not yaml
and (unique_id := trigger_entity_config.get(CONF_UNIQUE_ID)):
165 entry_type=DeviceEntryType.SERVICE,
166 identifiers={(DOMAIN, unique_id)},
167 manufacturer=
"Scrape",
172 """Parse the html extraction in the executor."""
173 raw_data = self.coordinator.data
174 value: str | list[str] |
None
176 if self.
_attr_attr
is not None:
179 tag = raw_data.select(self.
_select_select)[self.
_index_index]
180 if tag.name
in (
"style",
"script",
"template"):
185 _LOGGER.warning(
"Index '%s' not found in %s", self.
_index_index, self.
entity_identity_id)
189 "Attribute '%s' not found in %s", self.
_attr_attr, self.
entity_identity_id
192 _LOGGER.debug(
"Parsed value: %s", value)
196 """Ensure the data from the initial update is reflected in the state."""
201 """Update state from the rest data."""
206 value = template.async_render_with_possible_json_value(value,
None)
209 SensorDeviceClass.DATE,
210 SensorDeviceClass.TIMESTAMP,
224 """Return if entity is available."""
225 available1 = CoordinatorEntity.available.fget(self)
226 available2 = ManualTriggerEntity.available.fget(self)
227 return bool(available1
and available2)
231 """Handle updated data from the coordinator."""
None async_added_to_hass(self)
None _async_update_from_rest_data(self)
None __init__(self, HomeAssistant hass, ScrapeCoordinator coordinator, ConfigType trigger_entity_config, str select, str|None attr, int index, Template|None value_template, bool yaml)
None _handle_coordinator_update(self)
SensorDeviceClass|None device_class(self)
str|None device_class(self)
None async_write_ha_state(self)
str|UndefinedType|None name(self)
None _process_manual_data(self, Any|None value=None)
None async_setup_entry(HomeAssistant hass, ScrapeConfigEntry entry, AddEntitiesCallback async_add_entities)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
datetime|date|None async_parse_date_datetime(str value, str entity_id, SensorDeviceClass|str|None device_class)