393 files
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .api import SeaTemperatureAPI, SeaTemperatureError
|
||||
from .api import SeaTemperatureAPI
|
||||
from .const import (
|
||||
CONF_AREA,
|
||||
CONF_CONTINENT,
|
||||
@@ -18,12 +16,9 @@ from .const import (
|
||||
CONF_PATH,
|
||||
CONF_PLACE,
|
||||
CONF_PLACE_ID,
|
||||
CONF_SCAN_INTERVAL_HOURS,
|
||||
DEFAULT_SCAN_INTERVAL_HOURS,
|
||||
DOMAIN,
|
||||
MAX_SCAN_INTERVAL_HOURS,
|
||||
MIN_SCAN_INTERVAL_HOURS,
|
||||
)
|
||||
from .coordinator import SeaTemperatureConfigEntry, SeaTemperatureCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -127,21 +122,6 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def async_scan_interval(entry: ConfigEntry) -> timedelta:
|
||||
"""Return the poll interval for an entry, honouring the options flow."""
|
||||
hours = entry.options.get(CONF_SCAN_INTERVAL_HOURS, DEFAULT_SCAN_INTERVAL_HOURS)
|
||||
try:
|
||||
hours = int(hours)
|
||||
except (TypeError, ValueError):
|
||||
hours = DEFAULT_SCAN_INTERVAL_HOURS
|
||||
if not MIN_SCAN_INTERVAL_HOURS <= hours <= MAX_SCAN_INTERVAL_HOURS:
|
||||
# The options flow bounds the form, but a hand-edited or older stored
|
||||
# value reaches this reader directly - clamp both ends, not just the
|
||||
# one that would hammer the site.
|
||||
hours = DEFAULT_SCAN_INTERVAL_HOURS
|
||||
return timedelta(hours=hours)
|
||||
|
||||
|
||||
def entry_location_key(entry: ConfigEntry) -> str:
|
||||
"""Return the key an entry's entity unique_ids are built from.
|
||||
|
||||
@@ -243,11 +223,17 @@ async def _async_migrate_unique_ids(hass: HomeAssistant, entry: ConfigEntry) ->
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate legacy config entries from place IDs to path-based locations."""
|
||||
if entry.version > 4:
|
||||
_LOGGER.error("Unsupported SeaTemperatures config entry version: %s", entry.version)
|
||||
_LOGGER.error(
|
||||
"Unsupported SeaTemperatures config entry version: %s", entry.version
|
||||
)
|
||||
return False
|
||||
|
||||
if entry.version < 2:
|
||||
_LOGGER.debug("Migrating SeaTemperatures entry %s from version %s", entry.entry_id, entry.version)
|
||||
_LOGGER.debug(
|
||||
"Migrating SeaTemperatures entry %s from version %s",
|
||||
entry.entry_id,
|
||||
entry.version,
|
||||
)
|
||||
|
||||
data = dict(entry.data)
|
||||
location_path = data.get(CONF_PATH)
|
||||
@@ -291,30 +277,6 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def _async_fetch(
|
||||
api: SeaTemperatureAPI, location_path: str | None, place_name: str
|
||||
) -> dict:
|
||||
"""Fetch one refresh, turning an API failure into a single logged one.
|
||||
|
||||
The coordinator logs an UpdateFailed once and stays quiet while the failure
|
||||
persists, so the API layer deliberately reports nothing of its own.
|
||||
"""
|
||||
if not location_path:
|
||||
raise UpdateFailed(
|
||||
"No location path configured. Remove and re-add the integration."
|
||||
)
|
||||
|
||||
try:
|
||||
data = await api.get_temperatures(location_path)
|
||||
except SeaTemperatureError as err:
|
||||
raise UpdateFailed(str(err)) from err
|
||||
|
||||
if not data:
|
||||
raise UpdateFailed(f"Failed to fetch data for place {place_name}")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Reload the entry after the options flow saved a new poll interval.
|
||||
|
||||
@@ -327,45 +289,29 @@ async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> Non
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: SeaTemperatureConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Sea Temperature from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
# Registered before the first refresh so an options save during a slow or
|
||||
# failing setup is not silently dropped; async_on_unload drops the
|
||||
# subscription with the entry rather than stacking one per reload.
|
||||
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
|
||||
|
||||
place_name = entry.data.get(CONF_PLACE, "Unknown")
|
||||
location_path = entry.data.get(CONF_PATH)
|
||||
location_key = entry_location_key(entry)
|
||||
|
||||
api = SeaTemperatureAPI(hass)
|
||||
|
||||
async def async_update_data():
|
||||
"""Fetch data from API."""
|
||||
return await _async_fetch(api, location_path, place_name)
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"seatemperatures_{location_key}",
|
||||
update_method=async_update_data,
|
||||
update_interval=async_scan_interval(entry),
|
||||
)
|
||||
|
||||
coordinator = SeaTemperatureCoordinator(hass, entry, entry_location_key(entry))
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SeaTemperatureConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
# Core drops entry.runtime_data once the unload succeeded, so there is no
|
||||
# hass.data bookkeeping left to undo here.
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
Reference in New Issue
Block a user