Skip to content

Commit a446d8a

Browse files
Add fire sensors to smhi (home-assistant#153224)
1 parent b4a31fc commit a446d8a

File tree

13 files changed

+2425
-44
lines changed

13 files changed

+2425
-44
lines changed

homeassistant/components/smhi/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
)
1010
from homeassistant.core import HomeAssistant
1111

12-
from .coordinator import SMHIConfigEntry, SMHIDataUpdateCoordinator
12+
from .coordinator import (
13+
SMHIConfigEntry,
14+
SMHIDataUpdateCoordinator,
15+
SMHIFireDataUpdateCoordinator,
16+
)
1317

1418
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
1519

@@ -24,7 +28,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: SMHIConfigEntry) -> bool
2428

2529
coordinator = SMHIDataUpdateCoordinator(hass, entry)
2630
await coordinator.async_config_entry_first_refresh()
27-
entry.runtime_data = coordinator
31+
fire_coordinator = SMHIFireDataUpdateCoordinator(hass, entry)
32+
await fire_coordinator.async_config_entry_first_refresh()
33+
entry.runtime_data = (coordinator, fire_coordinator)
2834

2935
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
3036
return True

homeassistant/components/smhi/coordinator.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
import asyncio
66
from dataclasses import dataclass
77

8-
from pysmhi import SMHIForecast, SmhiForecastException, SMHIPointForecast
8+
from pysmhi import (
9+
SMHIFireForecast,
10+
SmhiFireForecastException,
11+
SMHIFirePointForecast,
12+
SMHIForecast,
13+
SmhiForecastException,
14+
SMHIPointForecast,
15+
)
916

1017
from homeassistant.config_entries import ConfigEntry
1118
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE
@@ -15,7 +22,9 @@
1522

1623
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT
1724

18-
type SMHIConfigEntry = ConfigEntry[SMHIDataUpdateCoordinator]
25+
type SMHIConfigEntry = ConfigEntry[
26+
tuple[SMHIDataUpdateCoordinator, SMHIFireDataUpdateCoordinator]
27+
]
1928

2029

2130
@dataclass
@@ -27,6 +36,14 @@ class SMHIForecastData:
2736
twice_daily: list[SMHIForecast]
2837

2938

39+
@dataclass
40+
class SMHIFireForecastData:
41+
"""Dataclass for SMHI fire data."""
42+
43+
fire_daily: list[SMHIFireForecast]
44+
fire_hourly: list[SMHIFireForecast]
45+
46+
3047
class SMHIDataUpdateCoordinator(DataUpdateCoordinator[SMHIForecastData]):
3148
"""A SMHI Data Update Coordinator."""
3249

@@ -71,3 +88,49 @@ async def _async_update_data(self) -> SMHIForecastData:
7188
def current(self) -> SMHIForecast:
7289
"""Return the current metrics."""
7390
return self.data.daily[0]
91+
92+
93+
class SMHIFireDataUpdateCoordinator(DataUpdateCoordinator[SMHIFireForecastData]):
94+
"""A SMHI Fire Data Update Coordinator."""
95+
96+
config_entry: SMHIConfigEntry
97+
98+
def __init__(self, hass: HomeAssistant, config_entry: SMHIConfigEntry) -> None:
99+
"""Initialize the SMHI coordinator."""
100+
super().__init__(
101+
hass,
102+
LOGGER,
103+
config_entry=config_entry,
104+
name=DOMAIN,
105+
update_interval=DEFAULT_SCAN_INTERVAL,
106+
)
107+
self._smhi_fire_api = SMHIFirePointForecast(
108+
config_entry.data[CONF_LOCATION][CONF_LONGITUDE],
109+
config_entry.data[CONF_LOCATION][CONF_LATITUDE],
110+
session=aiohttp_client.async_get_clientsession(hass),
111+
)
112+
113+
async def _async_update_data(self) -> SMHIFireForecastData:
114+
"""Fetch data from SMHI."""
115+
try:
116+
async with asyncio.timeout(TIMEOUT):
117+
_forecast_fire_daily = (
118+
await self._smhi_fire_api.async_get_daily_forecast()
119+
)
120+
_forecast_fire_hourly = (
121+
await self._smhi_fire_api.async_get_hourly_forecast()
122+
)
123+
except SmhiFireForecastException as ex:
124+
raise UpdateFailed(
125+
"Failed to retrieve the forecast from the SMHI API"
126+
) from ex
127+
128+
return SMHIFireForecastData(
129+
fire_daily=_forecast_fire_daily,
130+
fire_hourly=_forecast_fire_hourly,
131+
)
132+
133+
@property
134+
def fire_current(self) -> SMHIFireForecast:
135+
"""Return the current fire metrics."""
136+
return self.data.fire_daily[0]

homeassistant/components/smhi/entity.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66

77
from homeassistant.core import callback
88
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
9+
from homeassistant.helpers.entity import Entity
910
from homeassistant.helpers.update_coordinator import CoordinatorEntity
1011

1112
from .const import DOMAIN
12-
from .coordinator import SMHIDataUpdateCoordinator
13+
from .coordinator import SMHIDataUpdateCoordinator, SMHIFireDataUpdateCoordinator
1314

1415

15-
class SmhiWeatherBaseEntity(CoordinatorEntity[SMHIDataUpdateCoordinator]):
16+
class SmhiWeatherBaseEntity(Entity):
1617
"""Representation of a base weather entity."""
1718

1819
_attr_attribution = "Swedish weather institute (SMHI)"
@@ -22,10 +23,8 @@ def __init__(
2223
self,
2324
latitude: str,
2425
longitude: str,
25-
coordinator: SMHIDataUpdateCoordinator,
2626
) -> None:
2727
"""Initialize the SMHI base weather entity."""
28-
super().__init__(coordinator)
2928
self._attr_unique_id = f"{latitude}, {longitude}"
3029
self._attr_device_info = DeviceInfo(
3130
entry_type=DeviceEntryType.SERVICE,
@@ -36,12 +35,50 @@ def __init__(
3635
)
3736
self.update_entity_data()
3837

38+
@abstractmethod
39+
def update_entity_data(self) -> None:
40+
"""Refresh the entity data."""
41+
42+
43+
class SmhiWeatherEntity(
44+
CoordinatorEntity[SMHIDataUpdateCoordinator], SmhiWeatherBaseEntity
45+
):
46+
"""Representation of a weather entity."""
47+
48+
def __init__(
49+
self,
50+
latitude: str,
51+
longitude: str,
52+
coordinator: SMHIDataUpdateCoordinator,
53+
) -> None:
54+
"""Initialize the SMHI base weather entity."""
55+
super().__init__(coordinator)
56+
SmhiWeatherBaseEntity.__init__(self, latitude, longitude)
57+
3958
@callback
4059
def _handle_coordinator_update(self) -> None:
4160
"""Handle updated data from the coordinator."""
4261
self.update_entity_data()
4362
super()._handle_coordinator_update()
4463

45-
@abstractmethod
46-
def update_entity_data(self) -> None:
47-
"""Refresh the entity data."""
64+
65+
class SmhiFireEntity(
66+
CoordinatorEntity[SMHIFireDataUpdateCoordinator], SmhiWeatherBaseEntity
67+
):
68+
"""Representation of a weather entity."""
69+
70+
def __init__(
71+
self,
72+
latitude: str,
73+
longitude: str,
74+
coordinator: SMHIFireDataUpdateCoordinator,
75+
) -> None:
76+
"""Initialize the SMHI base weather entity."""
77+
super().__init__(coordinator)
78+
SmhiWeatherBaseEntity.__init__(self, latitude, longitude)
79+
80+
@callback
81+
def _handle_coordinator_update(self) -> None:
82+
"""Handle updated data from the coordinator."""
83+
self.update_entity_data()
84+
super()._handle_coordinator_update()

homeassistant/components/smhi/icons.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,42 @@
11
{
22
"entity": {
33
"sensor": {
4+
"build_up_index": {
5+
"default": "mdi:grass"
6+
},
7+
"drought_code": {
8+
"default": "mdi:grass"
9+
},
10+
"duff_moisture_code": {
11+
"default": "mdi:grass"
12+
},
13+
"fine_fuel_moisture_code": {
14+
"default": "mdi:grass"
15+
},
16+
"fire_weather_index": {
17+
"default": "mdi:pine-tree-fire"
18+
},
19+
"forestdry": {
20+
"default": "mdi:forest"
21+
},
422
"frozen_precipitation": {
523
"default": "mdi:weather-snowy-rainy"
624
},
25+
"fwi": {
26+
"default": "mdi:pine-tree-fire"
27+
},
28+
"fwiindex": {
29+
"default": "mdi:pine-tree-fire"
30+
},
31+
"grassfire": {
32+
"default": "mdi:fire-circle"
33+
},
734
"high_cloud": {
835
"default": "mdi:cloud-arrow-up"
936
},
37+
"initial_spread_index": {
38+
"default": "mdi:grass"
39+
},
1040
"low_cloud": {
1141
"default": "mdi:cloud-arrow-down"
1242
},
@@ -16,6 +46,9 @@
1646
"precipitation_category": {
1747
"default": "mdi:weather-pouring"
1848
},
49+
"rate_of_spread": {
50+
"default": "mdi:grass"
51+
},
1952
"thunder": {
2053
"default": "mdi:weather-lightning"
2154
},

0 commit comments

Comments
 (0)