diff --git a/homeassistant/components/weather/__init__.py b/homeassistant/components/weather/__init__.py index c4a6a0ad777..45b5cbe9fba 100644 --- a/homeassistant/components/weather/__init__.py +++ b/homeassistant/components/weather/__init__.py @@ -40,6 +40,7 @@ from .const import ( ATTR_WEATHER_PRESSURE_UNIT, ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_TEMPERATURE_UNIT, + ATTR_WEATHER_UV_INDEX, ATTR_WEATHER_VISIBILITY, ATTR_WEATHER_VISIBILITY_UNIT, ATTR_WEATHER_WIND_BEARING, @@ -93,6 +94,7 @@ ATTR_FORECAST_WIND_SPEED: Final = "wind_speed" ATTR_FORECAST_NATIVE_DEW_POINT: Final = "native_dew_point" ATTR_FORECAST_DEW_POINT: Final = "dew_point" ATTR_FORECAST_CLOUD_COVERAGE: Final = "cloud_coverage" +ATTR_FORECAST_UV_INDEX: Final = "uv_index" ENTITY_ID_FORMAT = DOMAIN + ".{}" @@ -146,6 +148,7 @@ class Forecast(TypedDict, total=False): native_wind_speed: float | None wind_speed: None native_dew_point: float | None + uv_index: float | None async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @@ -184,6 +187,7 @@ class WeatherEntity(Entity): _attr_humidity: float | None = None _attr_ozone: float | None = None _attr_cloud_coverage: int | None = None + _attr_uv_index: float | None = None _attr_precision: float _attr_pressure: None = ( None # Provide backwards compatibility. Use _attr_native_pressure @@ -503,6 +507,11 @@ class WeatherEntity(Entity): """Return the Cloud coverage in %.""" return self._attr_cloud_coverage + @property + def uv_index(self) -> float | None: + """Return the UV index.""" + return self._attr_uv_index + @final @property def visibility(self) -> float | None: @@ -680,6 +689,9 @@ class WeatherEntity(Entity): if (cloud_coverage := self.cloud_coverage) is not None: data[ATTR_WEATHER_CLOUD_COVERAGE] = cloud_coverage + if (uv_index := self.uv_index) is not None: + data[ATTR_WEATHER_UV_INDEX] = uv_index + if (pressure := self.native_pressure) is not None: from_unit = self.native_pressure_unit or self._default_pressure_unit to_unit = self._pressure_unit diff --git a/homeassistant/components/weather/const.py b/homeassistant/components/weather/const.py index b995ce2b729..759021741ff 100644 --- a/homeassistant/components/weather/const.py +++ b/homeassistant/components/weather/const.py @@ -34,6 +34,7 @@ ATTR_WEATHER_WIND_SPEED = "wind_speed" ATTR_WEATHER_WIND_SPEED_UNIT = "wind_speed_unit" ATTR_WEATHER_PRECIPITATION_UNIT = "precipitation_unit" ATTR_WEATHER_CLOUD_COVERAGE = "cloud_coverage" +ATTR_WEATHER_UV_INDEX = "uv_index" DOMAIN: Final = "weather" diff --git a/homeassistant/components/weather/strings.json b/homeassistant/components/weather/strings.json index 26ccd731828..21029c77284 100644 --- a/homeassistant/components/weather/strings.json +++ b/homeassistant/components/weather/strings.json @@ -71,6 +71,9 @@ }, "wind_speed_unit": { "name": "Wind speed unit" + }, + "uv_index": { + "name": "UV index" } } } diff --git a/tests/components/weather/test_init.py b/tests/components/weather/test_init.py index 5ed6a02f24b..53753ad4a72 100644 --- a/tests/components/weather/test_init.py +++ b/tests/components/weather/test_init.py @@ -13,6 +13,7 @@ from homeassistant.components.weather import ( ATTR_FORECAST_PRESSURE, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, + ATTR_FORECAST_UV_INDEX, ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_GUST_SPEED, ATTR_FORECAST_WIND_SPEED, @@ -23,6 +24,7 @@ from homeassistant.components.weather import ( ATTR_WEATHER_PRESSURE_UNIT, ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_TEMPERATURE_UNIT, + ATTR_WEATHER_UV_INDEX, ATTR_WEATHER_VISIBILITY, ATTR_WEATHER_VISIBILITY_UNIT, ATTR_WEATHER_WIND_BEARING, @@ -583,7 +585,7 @@ async def test_precipitation_no_unit( ) -async def test_wind_bearing_ozone_and_cloud_coverage( +async def test_wind_bearing_ozone_and_cloud_coverage_and_uv_index( hass: HomeAssistant, enable_custom_integrations: None, ) -> None: @@ -591,18 +593,23 @@ async def test_wind_bearing_ozone_and_cloud_coverage( wind_bearing_value = 180 ozone_value = 10 cloud_coverage = 75 + uv_index = 1.2 entity0 = await create_entity( hass, wind_bearing=wind_bearing_value, ozone=ozone_value, cloud_coverage=cloud_coverage, + uv_index=uv_index, ) state = hass.states.get(entity0.entity_id) + forecast = state.attributes[ATTR_FORECAST][0] assert float(state.attributes[ATTR_WEATHER_WIND_BEARING]) == 180 assert float(state.attributes[ATTR_WEATHER_OZONE]) == 10 assert float(state.attributes[ATTR_WEATHER_CLOUD_COVERAGE]) == 75 + assert float(state.attributes[ATTR_WEATHER_UV_INDEX]) == 1.2 + assert float(forecast[ATTR_FORECAST_UV_INDEX]) == 1.2 async def test_humidity( diff --git a/tests/testing_config/custom_components/test/weather.py b/tests/testing_config/custom_components/test/weather.py index a5c49fb92c2..df6a43ad40c 100644 --- a/tests/testing_config/custom_components/test/weather.py +++ b/tests/testing_config/custom_components/test/weather.py @@ -19,6 +19,7 @@ from homeassistant.components.weather import ( ATTR_FORECAST_PRESSURE, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, + ATTR_FORECAST_UV_INDEX, ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_SPEED, Forecast, @@ -111,6 +112,11 @@ class MockWeather(MockEntity, WeatherEntity): """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.""" @@ -228,6 +234,7 @@ class MockWeatherMockForecast(MockWeather): 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" ),