"""Weather component that handles meteorological data for your location."""
from __future__ import annotations

from collections.abc import Callable
from contextlib import suppress
from dataclasses import dataclass
from datetime import timedelta
import inspect
import logging
from typing import Any, Final, TypedDict, final

from typing_extensions import Required

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    PRECISION_HALVES,
    PRECISION_TENTHS,
    PRECISION_WHOLE,
    UnitOfLength,
    UnitOfPrecipitationDepth,
    UnitOfPressure,
    UnitOfSpeed,
    UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.config_validation import (  # noqa: F401
    PLATFORM_SCHEMA,
    PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.unit_conversion import (
    DistanceConverter,
    PressureConverter,
    SpeedConverter,
    TemperatureConverter,
)
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM

_LOGGER = logging.getLogger(__name__)

ATTR_CONDITION_CLASS = "condition_class"
ATTR_CONDITION_CLEAR_NIGHT = "clear-night"
ATTR_CONDITION_CLOUDY = "cloudy"
ATTR_CONDITION_EXCEPTIONAL = "exceptional"
ATTR_CONDITION_FOG = "fog"
ATTR_CONDITION_HAIL = "hail"
ATTR_CONDITION_LIGHTNING = "lightning"
ATTR_CONDITION_LIGHTNING_RAINY = "lightning-rainy"
ATTR_CONDITION_PARTLYCLOUDY = "partlycloudy"
ATTR_CONDITION_POURING = "pouring"
ATTR_CONDITION_RAINY = "rainy"
ATTR_CONDITION_SNOWY = "snowy"
ATTR_CONDITION_SNOWY_RAINY = "snowy-rainy"
ATTR_CONDITION_SUNNY = "sunny"
ATTR_CONDITION_WINDY = "windy"
ATTR_CONDITION_WINDY_VARIANT = "windy-variant"
ATTR_FORECAST = "forecast"
ATTR_FORECAST_CONDITION: Final = "condition"
ATTR_FORECAST_NATIVE_PRECIPITATION: Final = "native_precipitation"
ATTR_FORECAST_PRECIPITATION: Final = "precipitation"
ATTR_FORECAST_PRECIPITATION_PROBABILITY: Final = "precipitation_probability"
ATTR_FORECAST_NATIVE_PRESSURE: Final = "native_pressure"
ATTR_FORECAST_PRESSURE: Final = "pressure"
ATTR_FORECAST_NATIVE_TEMP: Final = "native_temperature"
ATTR_FORECAST_TEMP: Final = "temperature"
ATTR_FORECAST_NATIVE_TEMP_LOW: Final = "native_templow"
ATTR_FORECAST_TEMP_LOW: Final = "templow"
ATTR_FORECAST_TIME: Final = "datetime"
ATTR_FORECAST_WIND_BEARING: Final = "wind_bearing"
ATTR_FORECAST_NATIVE_WIND_SPEED: Final = "native_wind_speed"
ATTR_FORECAST_WIND_SPEED: Final = "wind_speed"
ATTR_WEATHER_HUMIDITY = "humidity"
ATTR_WEATHER_OZONE = "ozone"
ATTR_WEATHER_PRESSURE = "pressure"
ATTR_WEATHER_PRESSURE_UNIT = "pressure_unit"
ATTR_WEATHER_TEMPERATURE = "temperature"
ATTR_WEATHER_TEMPERATURE_UNIT = "temperature_unit"
ATTR_WEATHER_VISIBILITY = "visibility"
ATTR_WEATHER_VISIBILITY_UNIT = "visibility_unit"
ATTR_WEATHER_WIND_BEARING = "wind_bearing"
ATTR_WEATHER_WIND_SPEED = "wind_speed"
ATTR_WEATHER_WIND_SPEED_UNIT = "wind_speed_unit"
ATTR_WEATHER_PRECIPITATION_UNIT = "precipitation_unit"

DOMAIN = "weather"

ENTITY_ID_FORMAT = DOMAIN + ".{}"

SCAN_INTERVAL = timedelta(seconds=30)

ROUNDING_PRECISION = 2

VALID_UNITS_PRESSURE: set[str] = {
    UnitOfPressure.HPA,
    UnitOfPressure.MBAR,
    UnitOfPressure.INHG,
    UnitOfPressure.MMHG,
}
VALID_UNITS_TEMPERATURE: set[str] = {
    UnitOfTemperature.CELSIUS,
    UnitOfTemperature.FAHRENHEIT,
}
VALID_UNITS_PRECIPITATION: set[str] = {
    UnitOfPrecipitationDepth.MILLIMETERS,
    UnitOfPrecipitationDepth.INCHES,
}
VALID_UNITS_VISIBILITY: set[str] = {
    UnitOfLength.KILOMETERS,
    UnitOfLength.MILES,
}
VALID_UNITS_WIND_SPEED: set[str] = {
    UnitOfSpeed.FEET_PER_SECOND,
    UnitOfSpeed.KILOMETERS_PER_HOUR,
    UnitOfSpeed.KNOTS,
    UnitOfSpeed.METERS_PER_SECOND,
    UnitOfSpeed.MILES_PER_HOUR,
}

UNIT_CONVERSIONS: dict[str, Callable[[float, str, str], float]] = {
    ATTR_WEATHER_PRESSURE_UNIT: PressureConverter.convert,
    ATTR_WEATHER_TEMPERATURE_UNIT: TemperatureConverter.convert,
    ATTR_WEATHER_VISIBILITY_UNIT: DistanceConverter.convert,
    ATTR_WEATHER_PRECIPITATION_UNIT: DistanceConverter.convert,
    ATTR_WEATHER_WIND_SPEED_UNIT: SpeedConverter.convert,
}

VALID_UNITS: dict[str, set[str]] = {
    ATTR_WEATHER_PRESSURE_UNIT: VALID_UNITS_PRESSURE,
    ATTR_WEATHER_TEMPERATURE_UNIT: VALID_UNITS_TEMPERATURE,
    ATTR_WEATHER_VISIBILITY_UNIT: VALID_UNITS_VISIBILITY,
    ATTR_WEATHER_PRECIPITATION_UNIT: VALID_UNITS_PRECIPITATION,
    ATTR_WEATHER_WIND_SPEED_UNIT: VALID_UNITS_WIND_SPEED,
}

# mypy: disallow-any-generics


def round_temperature(temperature: float | None, precision: float) -> float | None:
    """Convert temperature into preferred precision for display."""
    if temperature is None:
        return None

    # Round in the units appropriate
    if precision == PRECISION_HALVES:
        temperature = round(temperature * 2) / 2.0
    elif precision == PRECISION_TENTHS:
        temperature = round(temperature, 1)
    # Integer as a fall back (PRECISION_WHOLE)
    else:
        temperature = round(temperature)

    return temperature


class Forecast(TypedDict, total=False):
    """Typed weather forecast dict.

    All attributes are in native units and old attributes kept
    for backwards compatibility.
    """

    condition: str | None
    datetime: Required[str]
    precipitation_probability: int | None
    native_precipitation: float | None
    precipitation: None
    native_pressure: float | None
    pressure: None
    native_temperature: float | None
    temperature: None
    native_templow: float | None
    templow: None
    wind_bearing: float | str | None
    native_wind_speed: float | None
    wind_speed: None


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the weather component."""
    component = hass.data[DOMAIN] = EntityComponent[WeatherEntity](
        _LOGGER, DOMAIN, hass, SCAN_INTERVAL
    )
    await component.async_setup(config)
    return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up a config entry."""
    component: EntityComponent[WeatherEntity] = hass.data[DOMAIN]
    return await component.async_setup_entry(entry)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    component: EntityComponent[WeatherEntity] = hass.data[DOMAIN]
    return await component.async_unload_entry(entry)


@dataclass
class WeatherEntityDescription(EntityDescription):
    """A class that describes weather entities."""


class WeatherEntity(Entity):
    """ABC for weather data."""

    entity_description: WeatherEntityDescription
    _attr_condition: str | None
    _attr_forecast: list[Forecast] | None = None
    _attr_humidity: float | None = None
    _attr_ozone: float | None = None
    _attr_precision: float
    _attr_pressure: None = (
        None  # Provide backwards compatibility. Use _attr_native_pressure
    )
    _attr_pressure_unit: None = (
        None  # Provide backwards compatibility. Use _attr_native_pressure_unit
    )
    _attr_state: None = None
    _attr_temperature: None = (
        None  # Provide backwards compatibility. Use _attr_native_temperature
    )
    _attr_temperature_unit: None = (
        None  # Provide backwards compatibility. Use _attr_native_temperature_unit
    )
    _attr_visibility: None = (
        None  # Provide backwards compatibility. Use _attr_native_visibility
    )
    _attr_visibility_unit: None = (
        None  # Provide backwards compatibility. Use _attr_native_visibility_unit
    )
    _attr_precipitation_unit: None = (
        None  # Provide backwards compatibility. Use _attr_native_precipitation_unit
    )
    _attr_wind_bearing: float | str | None = None
    _attr_wind_speed: None = (
        None  # Provide backwards compatibility. Use _attr_native_wind_speed
    )
    _attr_wind_speed_unit: None = (
        None  # Provide backwards compatibility. Use _attr_native_wind_speed_unit
    )

    _attr_native_pressure: float | None = None
    _attr_native_pressure_unit: str | None = None
    _attr_native_temperature: float | None = None
    _attr_native_temperature_unit: str | None = None
    _attr_native_visibility: float | None = None
    _attr_native_visibility_unit: str | None = None
    _attr_native_precipitation_unit: str | None = None
    _attr_native_wind_speed: float | None = None
    _attr_native_wind_speed_unit: str | None = None

    _weather_option_temperature_unit: str | None = None
    _weather_option_pressure_unit: str | None = None
    _weather_option_visibility_unit: str | None = None
    _weather_option_precipitation_unit: str | None = None
    _weather_option_wind_speed_unit: str | None = None

    def __init_subclass__(cls, **kwargs: Any) -> None:
        """Post initialisation processing."""
        super().__init_subclass__(**kwargs)
        _reported = False
        if any(
            method in cls.__dict__
            for method in (
                "_attr_temperature",
                "temperature",
                "_attr_temperature_unit",
                "temperature_unit",
                "_attr_pressure",
                "pressure",
                "_attr_pressure_unit",
                "pressure_unit",
                "_attr_wind_speed",
                "wind_speed",
                "_attr_wind_speed_unit",
                "wind_speed_unit",
                "_attr_visibility",
                "visibility",
                "_attr_visibility_unit",
                "visibility_unit",
                "_attr_precipitation_unit",
                "precipitation_unit",
            )
        ):
            if _reported is False:
                module = inspect.getmodule(cls)
                _reported = True
                if (
                    module
                    and module.__file__
                    and "custom_components" in module.__file__
                ):
                    report_issue = "report it to the custom integration author."
                else:
                    report_issue = (
                        "create a bug report at "
                        "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
                    )
                _LOGGER.warning(
                    (
                        "%s::%s is overriding deprecated methods on an instance of "
                        "WeatherEntity, this is not valid and will be unsupported "
                        "from Home Assistant 2023.1. Please %s"
                    ),
                    cls.__module__,
                    cls.__name__,
                    report_issue,
                )

    async def async_internal_added_to_hass(self) -> None:
        """Call when the sensor entity is added to hass."""
        await super().async_internal_added_to_hass()
        if not self.registry_entry:
            return
        self.async_registry_entry_updated()

    @final
    @property
    def temperature(self) -> float | None:
        """Return the temperature for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_temperature

    @property
    def native_temperature(self) -> float | None:
        """Return the temperature in native units."""
        if (temperature := self.temperature) is not None:
            return temperature

        return self._attr_native_temperature

    @property
    def native_temperature_unit(self) -> str | None:
        """Return the native unit of measurement for temperature."""
        if (temperature_unit := self.temperature_unit) is not None:
            return temperature_unit

        return self._attr_native_temperature_unit

    @final
    @property
    def temperature_unit(self) -> str | None:
        """Return the temperature unit for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_temperature_unit

    @final
    @property
    def _default_temperature_unit(self) -> str:
        """Return the default unit of measurement for temperature.

        Should not be set by integrations.
        """
        return self.hass.config.units.temperature_unit

    @final
    @property
    def _temperature_unit(self) -> str:
        """Return the converted unit of measurement for temperature.

        Should not be set by integrations.
        """
        if (
            weather_option_temperature_unit := self._weather_option_temperature_unit
        ) is not None:
            return weather_option_temperature_unit

        return self._default_temperature_unit

    @final
    @property
    def pressure(self) -> float | None:
        """Return the pressure for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_pressure

    @property
    def native_pressure(self) -> float | None:
        """Return the pressure in native units."""
        if (pressure := self.pressure) is not None:
            return pressure

        return self._attr_native_pressure

    @property
    def native_pressure_unit(self) -> str | None:
        """Return the native unit of measurement for pressure."""
        if (pressure_unit := self.pressure_unit) is not None:
            return pressure_unit

        return self._attr_native_pressure_unit

    @final
    @property
    def pressure_unit(self) -> str | None:
        """Return the pressure unit for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_pressure_unit

    @final
    @property
    def _default_pressure_unit(self) -> str:
        """Return the default unit of measurement for pressure.

        Should not be set by integrations.
        """
        if self.hass.config.units is US_CUSTOMARY_SYSTEM:
            return UnitOfPressure.INHG
        return UnitOfPressure.HPA

    @final
    @property
    def _pressure_unit(self) -> str:
        """Return the converted unit of measurement for pressure.

        Should not be set by integrations.
        """
        if (
            weather_option_pressure_unit := self._weather_option_pressure_unit
        ) is not None:
            return weather_option_pressure_unit

        return self._default_pressure_unit

    @property
    def humidity(self) -> float | None:
        """Return the humidity in native units."""
        return self._attr_humidity

    @final
    @property
    def wind_speed(self) -> float | None:
        """Return the wind_speed for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_wind_speed

    @property
    def native_wind_speed(self) -> float | None:
        """Return the wind speed in native units."""
        if (wind_speed := self.wind_speed) is not None:
            return wind_speed

        return self._attr_native_wind_speed

    @property
    def native_wind_speed_unit(self) -> str | None:
        """Return the native unit of measurement for wind speed."""
        if (wind_speed_unit := self.wind_speed_unit) is not None:
            return wind_speed_unit

        return self._attr_native_wind_speed_unit

    @final
    @property
    def wind_speed_unit(self) -> str | None:
        """Return the wind_speed unit for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_wind_speed_unit

    @final
    @property
    def _default_wind_speed_unit(self) -> str:
        """Return the default unit of measurement for wind speed.

        Should not be set by integrations.
        """
        if self.hass.config.units is US_CUSTOMARY_SYSTEM:
            return UnitOfSpeed.MILES_PER_HOUR
        return UnitOfSpeed.KILOMETERS_PER_HOUR

    @final
    @property
    def _wind_speed_unit(self) -> str:
        """Return the converted unit of measurement for wind speed.

        Should not be set by integrations.
        """
        if (
            weather_option_wind_speed_unit := self._weather_option_wind_speed_unit
        ) is not None:
            return weather_option_wind_speed_unit

        return self._default_wind_speed_unit

    @property
    def wind_bearing(self) -> float | str | None:
        """Return the wind bearing."""
        return self._attr_wind_bearing

    @property
    def ozone(self) -> float | None:
        """Return the ozone level."""
        return self._attr_ozone

    @final
    @property
    def visibility(self) -> float | None:
        """Return the visibility for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_visibility

    @property
    def native_visibility(self) -> float | None:
        """Return the visibility in native units."""
        if (visibility := self.visibility) is not None:
            return visibility

        return self._attr_native_visibility

    @property
    def native_visibility_unit(self) -> str | None:
        """Return the native unit of measurement for visibility."""
        if (visibility_unit := self.visibility_unit) is not None:
            return visibility_unit

        return self._attr_native_visibility_unit

    @final
    @property
    def visibility_unit(self) -> str | None:
        """Return the visibility unit for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_visibility_unit

    @final
    @property
    def _default_visibility_unit(self) -> str:
        """Return the default unit of measurement for visibility.

        Should not be set by integrations.
        """
        return self.hass.config.units.length_unit

    @final
    @property
    def _visibility_unit(self) -> str:
        """Return the converted unit of measurement for visibility.

        Should not be set by integrations.
        """
        if (
            weather_option_visibility_unit := self._weather_option_visibility_unit
        ) is not None:
            return weather_option_visibility_unit

        return self._default_visibility_unit

    @property
    def forecast(self) -> list[Forecast] | None:
        """Return the forecast in native units."""
        return self._attr_forecast

    @property
    def native_precipitation_unit(self) -> str | None:
        """Return the native unit of measurement for accumulated precipitation."""
        if (precipitation_unit := self.precipitation_unit) is not None:
            return precipitation_unit

        return self._attr_native_precipitation_unit

    @final
    @property
    def precipitation_unit(self) -> str | None:
        """Return the precipitation unit for backward compatibility.

        Should not be set by integrations.
        """
        return self._attr_precipitation_unit

    @final
    @property
    def _default_precipitation_unit(self) -> str:
        """Return the default unit of measurement for precipitation.

        Should not be set by integrations.
        """
        return self.hass.config.units.accumulated_precipitation_unit

    @final
    @property
    def _precipitation_unit(self) -> str:
        """Return the converted unit of measurement for precipitation.

        Should not be set by integrations.
        """
        if (
            weather_option_precipitation_unit := self._weather_option_precipitation_unit
        ) is not None:
            return weather_option_precipitation_unit

        return self._default_precipitation_unit

    @property
    def precision(self) -> float:
        """Return the precision of the temperature value, after unit conversion."""
        if hasattr(self, "_attr_precision"):
            return self._attr_precision
        return (
            PRECISION_TENTHS
            if self._temperature_unit == UnitOfTemperature.CELSIUS
            else PRECISION_WHOLE
        )

    @final
    @property
    def state_attributes(self) -> dict[str, Any]:
        """Return the state attributes, converted.

        Attributes are configured from native units to user-configured units.
        """
        data: dict[str, Any] = {}

        precision = self.precision

        if (temperature := self.native_temperature) is not None:
            from_unit = self.native_temperature_unit or self._default_temperature_unit
            to_unit = self._temperature_unit
            try:
                temperature_f = float(temperature)
                value_temp = UNIT_CONVERSIONS[ATTR_WEATHER_TEMPERATURE_UNIT](
                    temperature_f, from_unit, to_unit
                )
                data[ATTR_WEATHER_TEMPERATURE] = round_temperature(
                    value_temp, precision
                )
            except (TypeError, ValueError):
                data[ATTR_WEATHER_TEMPERATURE] = temperature

        data[ATTR_WEATHER_TEMPERATURE_UNIT] = self._temperature_unit

        if (humidity := self.humidity) is not None:
            data[ATTR_WEATHER_HUMIDITY] = round(humidity)

        if (ozone := self.ozone) is not None:
            data[ATTR_WEATHER_OZONE] = ozone

        if (pressure := self.native_pressure) is not None:
            from_unit = self.native_pressure_unit or self._default_pressure_unit
            to_unit = self._pressure_unit
            try:
                pressure_f = float(pressure)
                value_pressure = UNIT_CONVERSIONS[ATTR_WEATHER_PRESSURE_UNIT](
                    pressure_f, from_unit, to_unit
                )
                data[ATTR_WEATHER_PRESSURE] = round(value_pressure, ROUNDING_PRECISION)
            except (TypeError, ValueError):
                data[ATTR_WEATHER_PRESSURE] = pressure

        data[ATTR_WEATHER_PRESSURE_UNIT] = self._pressure_unit

        if (wind_bearing := self.wind_bearing) is not None:
            data[ATTR_WEATHER_WIND_BEARING] = wind_bearing

        if (wind_speed := self.native_wind_speed) is not None:
            from_unit = self.native_wind_speed_unit or self._default_wind_speed_unit
            to_unit = self._wind_speed_unit
            try:
                wind_speed_f = float(wind_speed)
                value_wind_speed = UNIT_CONVERSIONS[ATTR_WEATHER_WIND_SPEED_UNIT](
                    wind_speed_f, from_unit, to_unit
                )
                data[ATTR_WEATHER_WIND_SPEED] = round(
                    value_wind_speed, ROUNDING_PRECISION
                )
            except (TypeError, ValueError):
                data[ATTR_WEATHER_WIND_SPEED] = wind_speed

        data[ATTR_WEATHER_WIND_SPEED_UNIT] = self._wind_speed_unit

        if (visibility := self.native_visibility) is not None:
            from_unit = self.native_visibility_unit or self._default_visibility_unit
            to_unit = self._visibility_unit
            try:
                visibility_f = float(visibility)
                value_visibility = UNIT_CONVERSIONS[ATTR_WEATHER_VISIBILITY_UNIT](
                    visibility_f, from_unit, to_unit
                )
                data[ATTR_WEATHER_VISIBILITY] = round(
                    value_visibility, ROUNDING_PRECISION
                )
            except (TypeError, ValueError):
                data[ATTR_WEATHER_VISIBILITY] = visibility

        data[ATTR_WEATHER_VISIBILITY_UNIT] = self._visibility_unit
        data[ATTR_WEATHER_PRECIPITATION_UNIT] = self._precipitation_unit

        if self.forecast is not None:
            forecast: list[dict[str, Any]] = []
            for existing_forecast_entry in self.forecast:
                forecast_entry: dict[str, Any] = dict(existing_forecast_entry)

                temperature = forecast_entry.pop(
                    ATTR_FORECAST_NATIVE_TEMP, forecast_entry.get(ATTR_FORECAST_TEMP)
                )

                from_temp_unit = (
                    self.native_temperature_unit or self._default_temperature_unit
                )
                to_temp_unit = self._temperature_unit

                if temperature is None:
                    forecast_entry[ATTR_FORECAST_TEMP] = None
                else:
                    with suppress(TypeError, ValueError):
                        temperature_f = float(temperature)
                        value_temp = UNIT_CONVERSIONS[ATTR_WEATHER_TEMPERATURE_UNIT](
                            temperature_f,
                            from_temp_unit,
                            to_temp_unit,
                        )
                        forecast_entry[ATTR_FORECAST_TEMP] = round_temperature(
                            value_temp, precision
                        )

                if (
                    forecast_temp_low := forecast_entry.pop(
                        ATTR_FORECAST_NATIVE_TEMP_LOW,
                        forecast_entry.get(ATTR_FORECAST_TEMP_LOW),
                    )
                ) is not None:
                    with suppress(TypeError, ValueError):
                        forecast_temp_low_f = float(forecast_temp_low)
                        value_temp_low = UNIT_CONVERSIONS[
                            ATTR_WEATHER_TEMPERATURE_UNIT
                        ](
                            forecast_temp_low_f,
                            from_temp_unit,
                            to_temp_unit,
                        )

                        forecast_entry[ATTR_FORECAST_TEMP_LOW] = round_temperature(
                            value_temp_low, precision
                        )

                if (
                    forecast_pressure := forecast_entry.pop(
                        ATTR_FORECAST_NATIVE_PRESSURE,
                        forecast_entry.get(ATTR_FORECAST_PRESSURE),
                    )
                ) is not None:
                    from_pressure_unit = (
                        self.native_pressure_unit or self._default_pressure_unit
                    )
                    to_pressure_unit = self._pressure_unit
                    with suppress(TypeError, ValueError):
                        forecast_pressure_f = float(forecast_pressure)
                        forecast_entry[ATTR_FORECAST_PRESSURE] = round(
                            UNIT_CONVERSIONS[ATTR_WEATHER_PRESSURE_UNIT](
                                forecast_pressure_f,
                                from_pressure_unit,
                                to_pressure_unit,
                            ),
                            ROUNDING_PRECISION,
                        )

                if (
                    forecast_wind_speed := forecast_entry.pop(
                        ATTR_FORECAST_NATIVE_WIND_SPEED,
                        forecast_entry.get(ATTR_FORECAST_WIND_SPEED),
                    )
                ) is not None:
                    from_wind_speed_unit = (
                        self.native_wind_speed_unit or self._default_wind_speed_unit
                    )
                    to_wind_speed_unit = self._wind_speed_unit
                    with suppress(TypeError, ValueError):
                        forecast_wind_speed_f = float(forecast_wind_speed)
                        forecast_entry[ATTR_FORECAST_WIND_SPEED] = round(
                            UNIT_CONVERSIONS[ATTR_WEATHER_WIND_SPEED_UNIT](
                                forecast_wind_speed_f,
                                from_wind_speed_unit,
                                to_wind_speed_unit,
                            ),
                            ROUNDING_PRECISION,
                        )

                if (
                    forecast_precipitation := forecast_entry.pop(
                        ATTR_FORECAST_NATIVE_PRECIPITATION,
                        forecast_entry.get(ATTR_FORECAST_PRECIPITATION),
                    )
                ) is not None:
                    from_precipitation_unit = (
                        self.native_precipitation_unit
                        or self._default_precipitation_unit
                    )
                    to_precipitation_unit = self._precipitation_unit
                    with suppress(TypeError, ValueError):
                        forecast_precipitation_f = float(forecast_precipitation)
                        forecast_entry[ATTR_FORECAST_PRECIPITATION] = round(
                            UNIT_CONVERSIONS[ATTR_WEATHER_PRECIPITATION_UNIT](
                                forecast_precipitation_f,
                                from_precipitation_unit,
                                to_precipitation_unit,
                            ),
                            ROUNDING_PRECISION,
                        )

                forecast.append(forecast_entry)

            data[ATTR_FORECAST] = forecast

        return data

    @property
    @final
    def state(self) -> str | None:
        """Return the current state."""
        return self.condition

    @property
    def condition(self) -> str | None:
        """Return the current condition."""
        return self._attr_condition

    @callback
    def async_registry_entry_updated(self) -> None:
        """Run when the entity registry entry has been updated."""
        assert self.registry_entry
        self._weather_option_temperature_unit = None
        self._weather_option_pressure_unit = None
        self._weather_option_precipitation_unit = None
        self._weather_option_wind_speed_unit = None
        self._weather_option_visibility_unit = None
        if weather_options := self.registry_entry.options.get(DOMAIN):
            if (
                custom_unit_temperature := weather_options.get(
                    ATTR_WEATHER_TEMPERATURE_UNIT
                )
            ) and custom_unit_temperature in VALID_UNITS[ATTR_WEATHER_TEMPERATURE_UNIT]:
                self._weather_option_temperature_unit = custom_unit_temperature
            if (
                custom_unit_pressure := weather_options.get(ATTR_WEATHER_PRESSURE_UNIT)
            ) and custom_unit_pressure in VALID_UNITS[ATTR_WEATHER_PRESSURE_UNIT]:
                self._weather_option_pressure_unit = custom_unit_pressure
            if (
                custom_unit_precipitation := weather_options.get(
                    ATTR_WEATHER_PRECIPITATION_UNIT
                )
            ) and custom_unit_precipitation in VALID_UNITS[
                ATTR_WEATHER_PRECIPITATION_UNIT
            ]:
                self._weather_option_precipitation_unit = custom_unit_precipitation
            if (
                custom_unit_wind_speed := weather_options.get(
                    ATTR_WEATHER_WIND_SPEED_UNIT
                )
            ) and custom_unit_wind_speed in VALID_UNITS[ATTR_WEATHER_WIND_SPEED_UNIT]:
                self._weather_option_wind_speed_unit = custom_unit_wind_speed
            if (
                custom_unit_visibility := weather_options.get(
                    ATTR_WEATHER_VISIBILITY_UNIT
                )
            ) and custom_unit_visibility in VALID_UNITS[ATTR_WEATHER_VISIBILITY_UNIT]:
                self._weather_option_visibility_unit = custom_unit_visibility
