11"""Weather component that handles meteorological data for your location."""
22
3+ from __future__ import annotations
4+
35import abc
46from collections .abc import Callable , Iterable
57from contextlib import suppress
152154 bound = TimestampDataUpdateCoordinator [Any ],
153155 default = _DailyForecastUpdateCoordinatorT ,
154156)
157+ _MinutelyForecastUpdateCoordinatorT = TypeVar (
158+ "_MinutelyForecastUpdateCoordinatorT" ,
159+ bound = TimestampDataUpdateCoordinator [Any ],
160+ default = _DailyForecastUpdateCoordinatorT ,
161+ )
155162
156163# mypy: disallow-any-generics
157164
@@ -210,12 +217,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
210217 )
211218 component .async_register_entity_service (
212219 SERVICE_GET_FORECASTS ,
213- {vol .Required ("type" ): vol .In (("daily" , "hourly" , "twice_daily" ))},
220+ {vol .Required ("type" ): vol .In (("daily" , "hourly" , "twice_daily" , "minutely" ))},
214221 async_get_forecasts_service ,
215222 required_features = [
216223 WeatherEntityFeature .FORECAST_DAILY ,
217224 WeatherEntityFeature .FORECAST_HOURLY ,
218225 WeatherEntityFeature .FORECAST_TWICE_DAILY ,
226+ WeatherEntityFeature .FORECAST_MINUTELY ,
219227 ],
220228 supports_response = SupportsResponse .ONLY ,
221229 )
@@ -305,7 +313,7 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A
305313 _attr_native_dew_point : float | None = None
306314
307315 _forecast_listeners : dict [
308- Literal ["daily" , "hourly" , "twice_daily" ],
316+ Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
309317 list [Callable [[list [JsonValueType ] | None ], None ]],
310318 ]
311319
@@ -317,7 +325,12 @@ class WeatherEntity(Entity, PostInit, cached_properties=CACHED_PROPERTIES_WITH_A
317325
318326 def __post_init__ (self , * args : Any , ** kwargs : Any ) -> None :
319327 """Finish initializing."""
320- self ._forecast_listeners = {"daily" : [], "hourly" : [], "twice_daily" : []}
328+ self ._forecast_listeners = {
329+ "daily" : [],
330+ "hourly" : [],
331+ "twice_daily" : [],
332+ "minutely" : [],
333+ }
321334
322335 async def async_internal_added_to_hass (self ) -> None :
323336 """Call when the weather entity is added to hass."""
@@ -514,6 +527,10 @@ async def async_forecast_hourly(self) -> list[Forecast] | None:
514527 """Return the hourly forecast in native units."""
515528 raise NotImplementedError
516529
530+ async def async_forecast_minutely (self ) -> list [Forecast ] | None :
531+ """Return the minutely forecast in native units."""
532+ raise NotImplementedError
533+
517534 @cached_property
518535 def native_precipitation_unit (self ) -> str | None :
519536 """Return the native unit of measurement for accumulated precipitation."""
@@ -927,22 +944,22 @@ def async_registry_entry_updated(self) -> None:
927944 @callback
928945 def _async_subscription_started (
929946 self ,
930- forecast_type : Literal ["daily" , "hourly" , "twice_daily" ],
947+ forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
931948 ) -> None :
932949 """Start subscription to forecast_type."""
933950
934951 @callback
935952 def _async_subscription_ended (
936953 self ,
937- forecast_type : Literal ["daily" , "hourly" , "twice_daily" ],
954+ forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
938955 ) -> None :
939956 """End subscription to forecast_type."""
940957
941958 @final
942959 @callback
943960 def async_subscribe_forecast (
944961 self ,
945- forecast_type : Literal ["daily" , "hourly" , "twice_daily" ],
962+ forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
946963 forecast_listener : Callable [[list [JsonValueType ] | None ], None ],
947964 ) -> CALLBACK_TYPE :
948965 """Subscribe to forecast updates.
@@ -964,11 +981,13 @@ def unsubscribe() -> None:
964981
965982 @final
966983 async def async_update_listeners (
967- self , forecast_types : Iterable [Literal ["daily" , "hourly" , "twice_daily" ]] | None
984+ self ,
985+ forecast_types : Iterable [Literal ["daily" , "hourly" , "twice_daily" , "minutely" ]]
986+ | None ,
968987 ) -> None :
969988 """Push updated forecast to all listeners."""
970989 if forecast_types is None :
971- forecast_types = {"daily" , "hourly" , "twice_daily" }
990+ forecast_types = {"daily" , "hourly" , "twice_daily" , "minutely" }
972991 for forecast_type in forecast_types :
973992 if not self ._forecast_listeners [forecast_type ]:
974993 continue
@@ -1015,6 +1034,10 @@ async def async_get_forecasts_service(
10151034 if (supported_features & WeatherEntityFeature .FORECAST_HOURLY ) == 0 :
10161035 raise_unsupported_forecast (weather .entity_id , forecast_type )
10171036 native_forecast_list = await weather .async_forecast_hourly ()
1037+ elif forecast_type == "minutely" :
1038+ if (supported_features & WeatherEntityFeature .FORECAST_MINUTELY ) == 0 :
1039+ raise_unsupported_forecast (weather .entity_id , forecast_type )
1040+ native_forecast_list = await weather .async_forecast_minutely ()
10181041 else :
10191042 if (supported_features & WeatherEntityFeature .FORECAST_TWICE_DAILY ) == 0 :
10201043 raise_unsupported_forecast (weather .entity_id , forecast_type )
@@ -1036,6 +1059,7 @@ class CoordinatorWeatherEntity(
10361059 _DailyForecastUpdateCoordinatorT ,
10371060 _HourlyForecastUpdateCoordinatorT ,
10381061 _TwiceDailyForecastUpdateCoordinatorT ,
1062+ _MinutelyForecastUpdateCoordinatorT ,
10391063 ],
10401064):
10411065 """A class for weather entities using DataUpdateCoordinators."""
@@ -1048,26 +1072,31 @@ def __init__(
10481072 daily_coordinator : _DailyForecastUpdateCoordinatorT | None = None ,
10491073 hourly_coordinator : _HourlyForecastUpdateCoordinatorT | None = None ,
10501074 twice_daily_coordinator : _TwiceDailyForecastUpdateCoordinatorT | None = None ,
1075+ minutely_coordinator : _MinutelyForecastUpdateCoordinatorT | None = None ,
10511076 daily_forecast_valid : timedelta | None = None ,
10521077 hourly_forecast_valid : timedelta | None = None ,
10531078 twice_daily_forecast_valid : timedelta | None = None ,
1079+ minutely_forecast_valid : timedelta | None = None ,
10541080 ) -> None :
10551081 """Initialize."""
10561082 super ().__init__ (observation_coordinator , context )
10571083 self .forecast_coordinators = {
10581084 "daily" : daily_coordinator ,
10591085 "hourly" : hourly_coordinator ,
10601086 "twice_daily" : twice_daily_coordinator ,
1087+ "minutely" : minutely_coordinator ,
10611088 }
10621089 self .forecast_valid = {
10631090 "daily" : daily_forecast_valid ,
10641091 "hourly" : hourly_forecast_valid ,
10651092 "twice_daily" : twice_daily_forecast_valid ,
1093+ "minutely" : minutely_forecast_valid ,
10661094 }
10671095 self .unsub_forecast : dict [str , Callable [[], None ] | None ] = {
10681096 "daily" : None ,
10691097 "hourly" : None ,
10701098 "twice_daily" : None ,
1099+ "minutely" : None ,
10711100 }
10721101
10731102 async def async_added_to_hass (self ) -> None :
@@ -1076,9 +1105,10 @@ async def async_added_to_hass(self) -> None:
10761105 self .async_on_remove (partial (self ._remove_forecast_listener , "daily" ))
10771106 self .async_on_remove (partial (self ._remove_forecast_listener , "hourly" ))
10781107 self .async_on_remove (partial (self ._remove_forecast_listener , "twice_daily" ))
1108+ self .async_on_remove (partial (self ._remove_forecast_listener , "minutely" ))
10791109
10801110 def _remove_forecast_listener (
1081- self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" ]
1111+ self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ]
10821112 ) -> None :
10831113 """Remove weather forecast listener."""
10841114 if unsub_fn := self .unsub_forecast [forecast_type ]:
@@ -1088,7 +1118,7 @@ def _remove_forecast_listener(
10881118 @callback
10891119 def _async_subscription_started (
10901120 self ,
1091- forecast_type : Literal ["daily" , "hourly" , "twice_daily" ],
1121+ forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
10921122 ) -> None :
10931123 """Start subscription to forecast_type."""
10941124 if not (coordinator := self .forecast_coordinators [forecast_type ]):
@@ -1109,10 +1139,14 @@ def _handle_hourly_forecast_coordinator_update(self) -> None:
11091139 def _handle_twice_daily_forecast_coordinator_update (self ) -> None :
11101140 """Handle updated data from the twice daily forecast coordinator."""
11111141
1142+ @callback
1143+ def _handle_minutely_forecast_coordinator_update (self ) -> None :
1144+ """Handle updated data from the minutely forecast coordinator."""
1145+
11121146 @final
11131147 @callback
11141148 def _handle_forecast_update (
1115- self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" ]
1149+ self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ]
11161150 ) -> None :
11171151 """Update forecast data."""
11181152 coordinator = self .forecast_coordinators [forecast_type ]
@@ -1126,7 +1160,7 @@ def _handle_forecast_update(
11261160 @callback
11271161 def _async_subscription_ended (
11281162 self ,
1129- forecast_type : Literal ["daily" , "hourly" , "twice_daily" ],
1163+ forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ],
11301164 ) -> None :
11311165 """End subscription to forecast_type."""
11321166 self ._remove_forecast_listener (forecast_type )
@@ -1169,9 +1203,14 @@ def _async_forecast_twice_daily(self) -> list[Forecast] | None:
11691203 """Return the twice daily forecast in native units."""
11701204 raise NotImplementedError
11711205
1206+ @callback
1207+ def _async_forecast_minutely (self ) -> list [Forecast ] | None :
1208+ """Return the minutely forecast in native units."""
1209+ raise NotImplementedError
1210+
11721211 @final
11731212 async def _async_forecast (
1174- self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" ]
1213+ self , forecast_type : Literal ["daily" , "hourly" , "twice_daily" , "minutely" ]
11751214 ) -> list [Forecast ] | None :
11761215 """Return the forecast in native units."""
11771216 coordinator = self .forecast_coordinators [forecast_type ]
@@ -1198,6 +1237,11 @@ async def async_forecast_twice_daily(self) -> list[Forecast] | None:
11981237 """Return the twice daily forecast in native units."""
11991238 return await self ._async_forecast ("twice_daily" )
12001239
1240+ @final
1241+ async def async_forecast_minutely (self ) -> list [Forecast ] | None :
1242+ """Return the minutely forecast in native units."""
1243+ return await self ._async_forecast ("minutely" )
1244+
12011245
12021246class SingleCoordinatorWeatherEntity (
12031247 CoordinatorWeatherEntity [
0 commit comments