mirror of
https://github.com/home-assistant/core.git
synced 2025-04-29 03:37:51 +00:00
Add deprecation to legacy forecast for Weather (#97294)
* Add deprecation to legacy forecast * Mod _reported * issue * remove not need variable * kitchen_sink * 2024.3 * remove demo and mqtt * add checks * fix deprecation * remove variable * fix kitchen_sink * Fix deprecation warning * Expand issue * clean * Fix tests * fix kitchen_sink * not report on core integrations
This commit is contained in:
parent
b22b51fe3b
commit
7a690d7359
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
import asyncio
|
||||||
from collections.abc import Callable, Iterable
|
from collections.abc import Callable, Iterable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -47,6 +48,8 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
|||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||||
|
import homeassistant.helpers.issue_registry as ir
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import (
|
||||||
CoordinatorEntity,
|
CoordinatorEntity,
|
||||||
@ -318,6 +321,9 @@ class WeatherEntity(Entity, PostInit):
|
|||||||
Literal["daily", "hourly", "twice_daily"],
|
Literal["daily", "hourly", "twice_daily"],
|
||||||
list[Callable[[list[JsonValueType] | None], None]],
|
list[Callable[[list[JsonValueType] | None], None]],
|
||||||
]
|
]
|
||||||
|
__weather_legacy_forecast: bool = False
|
||||||
|
__weather_legacy_forecast_reported: bool = False
|
||||||
|
__report_issue: str
|
||||||
|
|
||||||
_weather_option_temperature_unit: str | None = None
|
_weather_option_temperature_unit: str | None = None
|
||||||
_weather_option_pressure_unit: str | None = None
|
_weather_option_pressure_unit: str | None = None
|
||||||
@ -381,6 +387,59 @@ class WeatherEntity(Entity, PostInit):
|
|||||||
cls.__name__,
|
cls.__name__,
|
||||||
report_issue,
|
report_issue,
|
||||||
)
|
)
|
||||||
|
if any(
|
||||||
|
method in cls.__dict__ for method in ("_attr_forecast", "forecast")
|
||||||
|
) and not any(
|
||||||
|
method in cls.__dict__
|
||||||
|
for method in (
|
||||||
|
"async_forecast_daily",
|
||||||
|
"async_forecast_hourly",
|
||||||
|
"async_forecast_twice_daily",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
cls.__weather_legacy_forecast = True
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def add_to_platform_start(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
platform: EntityPlatform,
|
||||||
|
parallel_updates: asyncio.Semaphore | None,
|
||||||
|
) -> None:
|
||||||
|
"""Start adding an entity to a platform."""
|
||||||
|
super().add_to_platform_start(hass, platform, parallel_updates)
|
||||||
|
_reported_forecast = False
|
||||||
|
if self.__weather_legacy_forecast and not _reported_forecast:
|
||||||
|
module = inspect.getmodule(self)
|
||||||
|
if module and module.__file__ and "custom_components" in module.__file__:
|
||||||
|
# Do not report on core integrations as they are already fixed or PR is open.
|
||||||
|
report_issue = "report it to the custom integration author."
|
||||||
|
_LOGGER.warning(
|
||||||
|
(
|
||||||
|
"%s::%s is using a forecast attribute on an instance of "
|
||||||
|
"WeatherEntity, this is deprecated and will be unsupported "
|
||||||
|
"from Home Assistant 2024.3. Please %s"
|
||||||
|
),
|
||||||
|
self.__module__,
|
||||||
|
self.entity_id,
|
||||||
|
report_issue,
|
||||||
|
)
|
||||||
|
ir.async_create_issue(
|
||||||
|
self.hass,
|
||||||
|
DOMAIN,
|
||||||
|
f"deprecated_weather_forecast_{self.platform.platform_name}",
|
||||||
|
breaks_in_ha_version="2024.3.0",
|
||||||
|
is_fixable=False,
|
||||||
|
is_persistent=False,
|
||||||
|
issue_domain=self.platform.platform_name,
|
||||||
|
severity=ir.IssueSeverity.WARNING,
|
||||||
|
translation_key="deprecated_weather_forecast",
|
||||||
|
translation_placeholders={
|
||||||
|
"platform": self.platform.platform_name,
|
||||||
|
"report_issue": report_issue,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
_reported_forecast = True
|
||||||
|
|
||||||
async def async_internal_added_to_hass(self) -> None:
|
async def async_internal_added_to_hass(self) -> None:
|
||||||
"""Call when the weather entity is added to hass."""
|
"""Call when the weather entity is added to hass."""
|
||||||
|
@ -98,5 +98,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"deprecated_weather_forecast": {
|
||||||
|
"title": "The {platform} integration is using deprecated forecast",
|
||||||
|
"description": "The integration `{platform}` is using the deprecated forecast attribute.\n\nPlease {report_issue}."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
import homeassistant.helpers.issue_registry as ir
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
from homeassistant.util.unit_conversion import (
|
from homeassistant.util.unit_conversion import (
|
||||||
@ -71,6 +72,9 @@ from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM
|
|||||||
from . import create_entity
|
from . import create_entity
|
||||||
|
|
||||||
from tests.testing_config.custom_components.test import weather as WeatherPlatform
|
from tests.testing_config.custom_components.test import weather as WeatherPlatform
|
||||||
|
from tests.testing_config.custom_components.test_weather import (
|
||||||
|
weather as NewWeatherPlatform,
|
||||||
|
)
|
||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
|
|
||||||
@ -1225,3 +1229,88 @@ async def test_get_forecast_unsupported(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
return_response=True,
|
return_response=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_issue_forecast_deprecated(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test the issue is raised on deprecated forecast attributes."""
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"native_temperature": 38,
|
||||||
|
"native_temperature_unit": UnitOfTemperature.CELSIUS,
|
||||||
|
}
|
||||||
|
platform: WeatherPlatform = getattr(hass.components, "test.weather")
|
||||||
|
caplog.clear()
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES.append(
|
||||||
|
platform.MockWeatherMockLegacyForecastOnly(
|
||||||
|
name="Testing",
|
||||||
|
entity_id="weather.testing",
|
||||||
|
condition=ATTR_CONDITION_SUNNY,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, "weather", {"weather": {"platform": "test", "name": "testing"}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert entity0.state == ATTR_CONDITION_SUNNY
|
||||||
|
|
||||||
|
issues = ir.async_get(hass)
|
||||||
|
issue = issues.async_get_issue("weather", "deprecated_weather_forecast_test")
|
||||||
|
assert issue
|
||||||
|
assert issue.issue_domain == "test"
|
||||||
|
assert issue.issue_id == "deprecated_weather_forecast_test"
|
||||||
|
assert issue.translation_placeholders == {
|
||||||
|
"platform": "test",
|
||||||
|
"report_issue": "report it to the custom integration author.",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"custom_components.test.weather::weather.testing is using a forecast attribute on an instance of WeatherEntity"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_issue_forecast_deprecated_no_logging(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test the no issue is raised on deprecated forecast attributes if new methods exist."""
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"native_temperature": 38,
|
||||||
|
"native_temperature_unit": UnitOfTemperature.CELSIUS,
|
||||||
|
}
|
||||||
|
platform: NewWeatherPlatform = getattr(hass.components, "test_weather.weather")
|
||||||
|
caplog.clear()
|
||||||
|
platform.init(empty=True)
|
||||||
|
platform.ENTITIES.append(
|
||||||
|
platform.MockWeatherMockForecast(
|
||||||
|
name="Test",
|
||||||
|
entity_id="weather.test",
|
||||||
|
condition=ATTR_CONDITION_SUNNY,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, "weather", {"weather": {"platform": "test_weather", "name": "test"}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert entity0.state == ATTR_CONDITION_SUNNY
|
||||||
|
|
||||||
|
assert "Setting up weather.test_weather" in caplog.text
|
||||||
|
assert (
|
||||||
|
"custom_components.test_weather.weather::weather.test is using a forecast attribute on an instance of WeatherEntity"
|
||||||
|
not in caplog.text
|
||||||
|
)
|
||||||
|
@ -298,6 +298,37 @@ class MockWeatherMockForecast(MockWeather):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MockWeatherMockLegacyForecastOnly(MockWeather):
|
||||||
|
"""Mock weather class with mocked legacy forecast."""
|
||||||
|
|
||||||
|
def __init__(self, **values: Any) -> None:
|
||||||
|
"""Initialize."""
|
||||||
|
super().__init__(**values)
|
||||||
|
self.forecast_list: list[Forecast] | None = [
|
||||||
|
{
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_APPARENT_TEMP: self.native_apparent_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_DEW_POINT: self.native_dew_point,
|
||||||
|
ATTR_FORECAST_CLOUD_COVERAGE: self.cloud_coverage,
|
||||||
|
ATTR_FORECAST_NATIVE_PRESSURE: self.native_pressure,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: self.native_wind_gust_speed,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED: self.native_wind_speed,
|
||||||
|
ATTR_FORECAST_WIND_BEARING: self.wind_bearing,
|
||||||
|
ATTR_FORECAST_UV_INDEX: self.uv_index,
|
||||||
|
ATTR_FORECAST_NATIVE_PRECIPITATION: self._values.get(
|
||||||
|
"native_precipitation"
|
||||||
|
),
|
||||||
|
ATTR_FORECAST_HUMIDITY: self.humidity,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def forecast(self) -> list[Forecast] | None:
|
||||||
|
"""Return the forecast."""
|
||||||
|
return self.forecast_list
|
||||||
|
|
||||||
|
|
||||||
class MockWeatherMockForecastCompat(MockWeatherCompat):
|
class MockWeatherMockForecastCompat(MockWeatherCompat):
|
||||||
"""Mock weather class with mocked forecast for compatibility check."""
|
"""Mock weather class with mocked forecast for compatibility check."""
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
"""An integration with Weather platform."""
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"domain": "test_weather",
|
||||||
|
"name": "Test Weather",
|
||||||
|
"documentation": "http://example.com",
|
||||||
|
"requirements": [],
|
||||||
|
"dependencies": [],
|
||||||
|
"codeowners": [],
|
||||||
|
"version": "1.2.3"
|
||||||
|
}
|
210
tests/testing_config/custom_components/test_weather/weather.py
Normal file
210
tests/testing_config/custom_components/test_weather/weather.py
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
"""Provide a mock weather platform.
|
||||||
|
|
||||||
|
Call init before using it in your tests to ensure clean test data.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from homeassistant.components.weather import (
|
||||||
|
ATTR_FORECAST_CLOUD_COVERAGE,
|
||||||
|
ATTR_FORECAST_HUMIDITY,
|
||||||
|
ATTR_FORECAST_IS_DAYTIME,
|
||||||
|
ATTR_FORECAST_NATIVE_APPARENT_TEMP,
|
||||||
|
ATTR_FORECAST_NATIVE_DEW_POINT,
|
||||||
|
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||||
|
ATTR_FORECAST_NATIVE_PRESSURE,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||||
|
ATTR_FORECAST_UV_INDEX,
|
||||||
|
ATTR_FORECAST_WIND_BEARING,
|
||||||
|
Forecast,
|
||||||
|
WeatherEntity,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tests.common import MockEntity
|
||||||
|
|
||||||
|
ENTITIES = []
|
||||||
|
|
||||||
|
|
||||||
|
def init(empty=False):
|
||||||
|
"""Initialize the platform with entities."""
|
||||||
|
global ENTITIES
|
||||||
|
ENTITIES = [] if empty else [MockWeatherMockForecast()]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(
|
||||||
|
hass, config, async_add_entities_callback, discovery_info=None
|
||||||
|
):
|
||||||
|
"""Return mock entities."""
|
||||||
|
async_add_entities_callback(ENTITIES)
|
||||||
|
|
||||||
|
|
||||||
|
class MockWeatherMockForecast(MockEntity, WeatherEntity):
|
||||||
|
"""Mock weather class."""
|
||||||
|
|
||||||
|
def __init__(self, **values: Any) -> None:
|
||||||
|
"""Initialize."""
|
||||||
|
super().__init__(**values)
|
||||||
|
self.forecast_list: list[Forecast] | None = [
|
||||||
|
{
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_APPARENT_TEMP: self.native_apparent_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_DEW_POINT: self.native_dew_point,
|
||||||
|
ATTR_FORECAST_CLOUD_COVERAGE: self.cloud_coverage,
|
||||||
|
ATTR_FORECAST_NATIVE_PRESSURE: self.native_pressure,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: self.native_wind_gust_speed,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED: self.native_wind_speed,
|
||||||
|
ATTR_FORECAST_WIND_BEARING: self.wind_bearing,
|
||||||
|
ATTR_FORECAST_UV_INDEX: self.uv_index,
|
||||||
|
ATTR_FORECAST_NATIVE_PRECIPITATION: self._values.get(
|
||||||
|
"native_precipitation"
|
||||||
|
),
|
||||||
|
ATTR_FORECAST_HUMIDITY: self.humidity,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def forecast(self) -> list[Forecast] | None:
|
||||||
|
"""Return the forecast."""
|
||||||
|
return self.forecast_list
|
||||||
|
|
||||||
|
async def async_forecast_daily(self) -> list[Forecast] | None:
|
||||||
|
"""Return the forecast_daily."""
|
||||||
|
return self.forecast_list
|
||||||
|
|
||||||
|
async def async_forecast_twice_daily(self) -> list[Forecast] | None:
|
||||||
|
"""Return the forecast_twice_daily."""
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_APPARENT_TEMP: self.native_apparent_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_DEW_POINT: self.native_dew_point,
|
||||||
|
ATTR_FORECAST_CLOUD_COVERAGE: self.cloud_coverage,
|
||||||
|
ATTR_FORECAST_NATIVE_PRESSURE: self.native_pressure,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: self.native_wind_gust_speed,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED: self.native_wind_speed,
|
||||||
|
ATTR_FORECAST_WIND_BEARING: self.wind_bearing,
|
||||||
|
ATTR_FORECAST_UV_INDEX: self.uv_index,
|
||||||
|
ATTR_FORECAST_NATIVE_PRECIPITATION: self._values.get(
|
||||||
|
"native_precipitation"
|
||||||
|
),
|
||||||
|
ATTR_FORECAST_HUMIDITY: self.humidity,
|
||||||
|
ATTR_FORECAST_IS_DAYTIME: self._values.get("is_daytime"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
async def async_forecast_hourly(self) -> list[Forecast] | None:
|
||||||
|
"""Return the forecast_hourly."""
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_APPARENT_TEMP: self.native_apparent_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW: self.native_temperature,
|
||||||
|
ATTR_FORECAST_NATIVE_DEW_POINT: self.native_dew_point,
|
||||||
|
ATTR_FORECAST_CLOUD_COVERAGE: self.cloud_coverage,
|
||||||
|
ATTR_FORECAST_NATIVE_PRESSURE: self.native_pressure,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: self.native_wind_gust_speed,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED: self.native_wind_speed,
|
||||||
|
ATTR_FORECAST_WIND_BEARING: self.wind_bearing,
|
||||||
|
ATTR_FORECAST_UV_INDEX: self.uv_index,
|
||||||
|
ATTR_FORECAST_NATIVE_PRECIPITATION: self._values.get(
|
||||||
|
"native_precipitation"
|
||||||
|
),
|
||||||
|
ATTR_FORECAST_HUMIDITY: self.humidity,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_temperature(self) -> float | None:
|
||||||
|
"""Return the platform temperature."""
|
||||||
|
return self._handle("native_temperature")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_apparent_temperature(self) -> float | None:
|
||||||
|
"""Return the platform apparent temperature."""
|
||||||
|
return self._handle("native_apparent_temperature")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_dew_point(self) -> float | None:
|
||||||
|
"""Return the platform dewpoint temperature."""
|
||||||
|
return self._handle("native_dew_point")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_temperature_unit(self) -> str | None:
|
||||||
|
"""Return the unit of measurement for temperature."""
|
||||||
|
return self._handle("native_temperature_unit")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_pressure(self) -> float | None:
|
||||||
|
"""Return the pressure."""
|
||||||
|
return self._handle("native_pressure")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_pressure_unit(self) -> str | None:
|
||||||
|
"""Return the unit of measurement for pressure."""
|
||||||
|
return self._handle("native_pressure_unit")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def humidity(self) -> float | None:
|
||||||
|
"""Return the humidity."""
|
||||||
|
return self._handle("humidity")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_wind_gust_speed(self) -> float | None:
|
||||||
|
"""Return the wind speed."""
|
||||||
|
return self._handle("native_wind_gust_speed")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_wind_speed(self) -> float | None:
|
||||||
|
"""Return the wind speed."""
|
||||||
|
return self._handle("native_wind_speed")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_wind_speed_unit(self) -> str | None:
|
||||||
|
"""Return the unit of measurement for wind speed."""
|
||||||
|
return self._handle("native_wind_speed_unit")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wind_bearing(self) -> float | str | None:
|
||||||
|
"""Return the wind bearing."""
|
||||||
|
return self._handle("wind_bearing")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ozone(self) -> float | None:
|
||||||
|
"""Return the ozone level."""
|
||||||
|
return self._handle("ozone")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cloud_coverage(self) -> float | None:
|
||||||
|
"""Return the cloud coverage in %."""
|
||||||
|
return self._handle("cloud_coverage")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uv_index(self) -> float | None:
|
||||||
|
"""Return the UV index."""
|
||||||
|
return self._handle("uv_index")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_visibility(self) -> float | None:
|
||||||
|
"""Return the visibility."""
|
||||||
|
return self._handle("native_visibility")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_visibility_unit(self) -> str | None:
|
||||||
|
"""Return the unit of measurement for visibility."""
|
||||||
|
return self._handle("native_visibility_unit")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_precipitation_unit(self) -> str | None:
|
||||||
|
"""Return the native unit of measurement for accumulated precipitation."""
|
||||||
|
return self._handle("native_precipitation_unit")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def condition(self) -> str | None:
|
||||||
|
"""Return the current condition."""
|
||||||
|
return self._handle("condition")
|
Loading…
x
Reference in New Issue
Block a user