mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 21:06:19 +00:00
Add fire sensors to smhi (#153224)
This commit is contained in:
@@ -9,7 +9,11 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import SMHIConfigEntry, SMHIDataUpdateCoordinator
|
||||
from .coordinator import (
|
||||
SMHIConfigEntry,
|
||||
SMHIDataUpdateCoordinator,
|
||||
SMHIFireDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
||||
|
||||
@@ -24,7 +28,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: SMHIConfigEntry) -> bool
|
||||
|
||||
coordinator = SMHIDataUpdateCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = coordinator
|
||||
fire_coordinator = SMHIFireDataUpdateCoordinator(hass, entry)
|
||||
await fire_coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = (coordinator, fire_coordinator)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
@@ -5,7 +5,14 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
|
||||
from pysmhi import SMHIForecast, SmhiForecastException, SMHIPointForecast
|
||||
from pysmhi import (
|
||||
SMHIFireForecast,
|
||||
SmhiFireForecastException,
|
||||
SMHIFirePointForecast,
|
||||
SMHIForecast,
|
||||
SmhiForecastException,
|
||||
SMHIPointForecast,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE
|
||||
@@ -15,7 +22,9 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT
|
||||
|
||||
type SMHIConfigEntry = ConfigEntry[SMHIDataUpdateCoordinator]
|
||||
type SMHIConfigEntry = ConfigEntry[
|
||||
tuple[SMHIDataUpdateCoordinator, SMHIFireDataUpdateCoordinator]
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -27,6 +36,14 @@ class SMHIForecastData:
|
||||
twice_daily: list[SMHIForecast]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SMHIFireForecastData:
|
||||
"""Dataclass for SMHI fire data."""
|
||||
|
||||
fire_daily: list[SMHIFireForecast]
|
||||
fire_hourly: list[SMHIFireForecast]
|
||||
|
||||
|
||||
class SMHIDataUpdateCoordinator(DataUpdateCoordinator[SMHIForecastData]):
|
||||
"""A SMHI Data Update Coordinator."""
|
||||
|
||||
@@ -71,3 +88,49 @@ class SMHIDataUpdateCoordinator(DataUpdateCoordinator[SMHIForecastData]):
|
||||
def current(self) -> SMHIForecast:
|
||||
"""Return the current metrics."""
|
||||
return self.data.daily[0]
|
||||
|
||||
|
||||
class SMHIFireDataUpdateCoordinator(DataUpdateCoordinator[SMHIFireForecastData]):
|
||||
"""A SMHI Fire Data Update Coordinator."""
|
||||
|
||||
config_entry: SMHIConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: SMHIConfigEntry) -> None:
|
||||
"""Initialize the SMHI coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
LOGGER,
|
||||
config_entry=config_entry,
|
||||
name=DOMAIN,
|
||||
update_interval=DEFAULT_SCAN_INTERVAL,
|
||||
)
|
||||
self._smhi_fire_api = SMHIFirePointForecast(
|
||||
config_entry.data[CONF_LOCATION][CONF_LONGITUDE],
|
||||
config_entry.data[CONF_LOCATION][CONF_LATITUDE],
|
||||
session=aiohttp_client.async_get_clientsession(hass),
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> SMHIFireForecastData:
|
||||
"""Fetch data from SMHI."""
|
||||
try:
|
||||
async with asyncio.timeout(TIMEOUT):
|
||||
_forecast_fire_daily = (
|
||||
await self._smhi_fire_api.async_get_daily_forecast()
|
||||
)
|
||||
_forecast_fire_hourly = (
|
||||
await self._smhi_fire_api.async_get_hourly_forecast()
|
||||
)
|
||||
except SmhiFireForecastException as ex:
|
||||
raise UpdateFailed(
|
||||
"Failed to retrieve the forecast from the SMHI API"
|
||||
) from ex
|
||||
|
||||
return SMHIFireForecastData(
|
||||
fire_daily=_forecast_fire_daily,
|
||||
fire_hourly=_forecast_fire_hourly,
|
||||
)
|
||||
|
||||
@property
|
||||
def fire_current(self) -> SMHIFireForecast:
|
||||
"""Return the current fire metrics."""
|
||||
return self.data.fire_daily[0]
|
||||
|
||||
@@ -6,13 +6,14 @@ from abc import abstractmethod
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SMHIDataUpdateCoordinator
|
||||
from .coordinator import SMHIDataUpdateCoordinator, SMHIFireDataUpdateCoordinator
|
||||
|
||||
|
||||
class SmhiWeatherBaseEntity(CoordinatorEntity[SMHIDataUpdateCoordinator]):
|
||||
class SmhiWeatherBaseEntity(Entity):
|
||||
"""Representation of a base weather entity."""
|
||||
|
||||
_attr_attribution = "Swedish weather institute (SMHI)"
|
||||
@@ -22,10 +23,8 @@ class SmhiWeatherBaseEntity(CoordinatorEntity[SMHIDataUpdateCoordinator]):
|
||||
self,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
coordinator: SMHIDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the SMHI base weather entity."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = f"{latitude}, {longitude}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
@@ -36,12 +35,50 @@ class SmhiWeatherBaseEntity(CoordinatorEntity[SMHIDataUpdateCoordinator]):
|
||||
)
|
||||
self.update_entity_data()
|
||||
|
||||
@abstractmethod
|
||||
def update_entity_data(self) -> None:
|
||||
"""Refresh the entity data."""
|
||||
|
||||
|
||||
class SmhiWeatherEntity(
|
||||
CoordinatorEntity[SMHIDataUpdateCoordinator], SmhiWeatherBaseEntity
|
||||
):
|
||||
"""Representation of a weather entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
coordinator: SMHIDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the SMHI base weather entity."""
|
||||
super().__init__(coordinator)
|
||||
SmhiWeatherBaseEntity.__init__(self, latitude, longitude)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self.update_entity_data()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@abstractmethod
|
||||
def update_entity_data(self) -> None:
|
||||
"""Refresh the entity data."""
|
||||
|
||||
class SmhiFireEntity(
|
||||
CoordinatorEntity[SMHIFireDataUpdateCoordinator], SmhiWeatherBaseEntity
|
||||
):
|
||||
"""Representation of a weather entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
coordinator: SMHIFireDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the SMHI base weather entity."""
|
||||
super().__init__(coordinator)
|
||||
SmhiWeatherBaseEntity.__init__(self, latitude, longitude)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self.update_entity_data()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@@ -1,12 +1,42 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"build_up_index": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"drought_code": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"duff_moisture_code": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"fine_fuel_moisture_code": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"fire_weather_index": {
|
||||
"default": "mdi:pine-tree-fire"
|
||||
},
|
||||
"forestdry": {
|
||||
"default": "mdi:forest"
|
||||
},
|
||||
"frozen_precipitation": {
|
||||
"default": "mdi:weather-snowy-rainy"
|
||||
},
|
||||
"fwi": {
|
||||
"default": "mdi:pine-tree-fire"
|
||||
},
|
||||
"fwiindex": {
|
||||
"default": "mdi:pine-tree-fire"
|
||||
},
|
||||
"grassfire": {
|
||||
"default": "mdi:fire-circle"
|
||||
},
|
||||
"high_cloud": {
|
||||
"default": "mdi:cloud-arrow-up"
|
||||
},
|
||||
"initial_spread_index": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"low_cloud": {
|
||||
"default": "mdi:cloud-arrow-down"
|
||||
},
|
||||
@@ -16,6 +46,9 @@
|
||||
"precipitation_category": {
|
||||
"default": "mdi:weather-pouring"
|
||||
},
|
||||
"rate_of_spread": {
|
||||
"default": "mdi:grass"
|
||||
},
|
||||
"thunder": {
|
||||
"default": "mdi:weather-lightning"
|
||||
},
|
||||
|
||||
@@ -10,19 +10,55 @@ from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE,
|
||||
CONF_LOCATION,
|
||||
CONF_LONGITUDE,
|
||||
PERCENTAGE,
|
||||
UnitOfSpeed,
|
||||
)
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE, PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .coordinator import SMHIConfigEntry, SMHIDataUpdateCoordinator
|
||||
from .entity import SmhiWeatherBaseEntity
|
||||
from .coordinator import (
|
||||
SMHIConfigEntry,
|
||||
SMHIDataUpdateCoordinator,
|
||||
SMHIFireDataUpdateCoordinator,
|
||||
)
|
||||
from .entity import SmhiFireEntity, SmhiWeatherEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
FWI_INDEX_MAP = {
|
||||
"1": "very_low",
|
||||
"2": "low",
|
||||
"3": "moderate",
|
||||
"4": "high",
|
||||
"5": "very_high",
|
||||
"6": "extreme",
|
||||
}
|
||||
GRASSFIRE_MAP = {
|
||||
"1": "snow_cover",
|
||||
"2": "season_over",
|
||||
"3": "low",
|
||||
"4": "moderate",
|
||||
"5": "high",
|
||||
"6": "very_high",
|
||||
}
|
||||
FORESTDRY_MAP = {
|
||||
"1": "very_wet",
|
||||
"2": "wet",
|
||||
"3": "moderate_wet",
|
||||
"4": "dry",
|
||||
"5": "very_dry",
|
||||
"6": "extremely_dry",
|
||||
}
|
||||
|
||||
def get_percentage_values(entity: SMHISensor, key: str) -> int | None:
|
||||
|
||||
def get_percentage_values(entity: SMHIWeatherSensor, key: str) -> int | None:
|
||||
"""Return percentage values in correct range."""
|
||||
value: int | None = entity.coordinator.current.get(key) # type: ignore[assignment]
|
||||
if value is not None and 0 <= value <= 100:
|
||||
@@ -32,49 +68,64 @@ def get_percentage_values(entity: SMHISensor, key: str) -> int | None:
|
||||
return None
|
||||
|
||||
|
||||
def get_fire_index_value(entity: SMHIFireSensor, key: str) -> str:
|
||||
"""Return index value as string."""
|
||||
value: int | None = entity.coordinator.fire_current.get(key) # type: ignore[assignment]
|
||||
if value is not None and value > 0:
|
||||
return str(int(value))
|
||||
return "0"
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SMHISensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes SMHI sensor entity."""
|
||||
class SMHIWeatherEntityDescription(SensorEntityDescription):
|
||||
"""Describes SMHI weather entity."""
|
||||
|
||||
value_fn: Callable[[SMHISensor], StateType | datetime]
|
||||
value_fn: Callable[[SMHIWeatherSensor], StateType | datetime]
|
||||
|
||||
|
||||
SENSOR_DESCRIPTIONS: tuple[SMHISensorEntityDescription, ...] = (
|
||||
SMHISensorEntityDescription(
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SMHIFireEntityDescription(SensorEntityDescription):
|
||||
"""Describes SMHI fire entity."""
|
||||
|
||||
value_fn: Callable[[SMHIFireSensor], StateType | datetime]
|
||||
|
||||
|
||||
WEATHER_SENSOR_DESCRIPTIONS: tuple[SMHIWeatherEntityDescription, ...] = (
|
||||
SMHIWeatherEntityDescription(
|
||||
key="thunder",
|
||||
translation_key="thunder",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "thunder"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="total_cloud",
|
||||
translation_key="total_cloud",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "total_cloud"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="low_cloud",
|
||||
translation_key="low_cloud",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "low_cloud"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="medium_cloud",
|
||||
translation_key="medium_cloud",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "medium_cloud"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="high_cloud",
|
||||
translation_key="high_cloud",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "high_cloud"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="precipitation_category",
|
||||
translation_key="precipitation_category",
|
||||
value_fn=lambda entity: str(
|
||||
@@ -83,13 +134,100 @@ SENSOR_DESCRIPTIONS: tuple[SMHISensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["0", "1", "2", "3", "4", "5", "6"],
|
||||
),
|
||||
SMHISensorEntityDescription(
|
||||
SMHIWeatherEntityDescription(
|
||||
key="frozen_precipitation",
|
||||
translation_key="frozen_precipitation",
|
||||
value_fn=lambda entity: get_percentage_values(entity, "frozen_precipitation"),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
)
|
||||
FIRE_SENSOR_DESCRIPTIONS: tuple[SMHIFireEntityDescription, ...] = (
|
||||
SMHIFireEntityDescription(
|
||||
key="fwiindex",
|
||||
translation_key="fwiindex",
|
||||
value_fn=(
|
||||
lambda entity: FWI_INDEX_MAP.get(get_fire_index_value(entity, "fwiindex"))
|
||||
),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=[*FWI_INDEX_MAP.values()],
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="fire_weather_index",
|
||||
translation_key="fire_weather_index",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("fwi"),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="initial_spread_index",
|
||||
translation_key="initial_spread_index",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("isi"),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="build_up_index",
|
||||
translation_key="build_up_index",
|
||||
value_fn=(
|
||||
lambda entity: entity.coordinator.fire_current.get(
|
||||
"bui" # codespell:ignore bui
|
||||
)
|
||||
),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="fine_fuel_moisture_code",
|
||||
translation_key="fine_fuel_moisture_code",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("ffmc"),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="duff_moisture_code",
|
||||
translation_key="duff_moisture_code",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("dmc"),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="drought_code",
|
||||
translation_key="drought_code",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("dc"),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="grassfire",
|
||||
translation_key="grassfire",
|
||||
value_fn=(
|
||||
lambda entity: GRASSFIRE_MAP.get(get_fire_index_value(entity, "grassfire"))
|
||||
),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=[*GRASSFIRE_MAP.values()],
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="rate_of_spread",
|
||||
translation_key="rate_of_spread",
|
||||
value_fn=lambda entity: entity.coordinator.fire_current.get("rn"),
|
||||
device_class=SensorDeviceClass.SPEED,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_MINUTE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SMHIFireEntityDescription(
|
||||
key="forestdry",
|
||||
translation_key="forestdry",
|
||||
value_fn=(
|
||||
lambda entity: FORESTDRY_MAP.get(get_fire_index_value(entity, "forestdry"))
|
||||
),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=[*FORESTDRY_MAP.values()],
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -99,30 +237,43 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up SMHI sensor platform."""
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = entry.runtime_data[0]
|
||||
fire_coordinator = entry.runtime_data[1]
|
||||
location = entry.data
|
||||
async_add_entities(
|
||||
SMHISensor(
|
||||
entities: list[SMHIWeatherSensor | SMHIFireSensor] = []
|
||||
entities.extend(
|
||||
SMHIWeatherSensor(
|
||||
location[CONF_LOCATION][CONF_LATITUDE],
|
||||
location[CONF_LOCATION][CONF_LONGITUDE],
|
||||
coordinator=coordinator,
|
||||
entity_description=description,
|
||||
)
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
for description in WEATHER_SENSOR_DESCRIPTIONS
|
||||
)
|
||||
entities.extend(
|
||||
SMHIFireSensor(
|
||||
location[CONF_LOCATION][CONF_LATITUDE],
|
||||
location[CONF_LOCATION][CONF_LONGITUDE],
|
||||
coordinator=fire_coordinator,
|
||||
entity_description=description,
|
||||
)
|
||||
for description in FIRE_SENSOR_DESCRIPTIONS
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
class SMHISensor(SmhiWeatherBaseEntity, SensorEntity):
|
||||
"""Representation of a SMHI Sensor."""
|
||||
|
||||
entity_description: SMHISensorEntityDescription
|
||||
class SMHIWeatherSensor(SmhiWeatherEntity, SensorEntity):
|
||||
"""Representation of a SMHI Weather Sensor."""
|
||||
|
||||
entity_description: SMHIWeatherEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
coordinator: SMHIDataUpdateCoordinator,
|
||||
entity_description: SMHISensorEntityDescription,
|
||||
entity_description: SMHIWeatherEntityDescription,
|
||||
) -> None:
|
||||
"""Initiate SMHI Sensor."""
|
||||
self.entity_description = entity_description
|
||||
@@ -137,3 +288,30 @@ class SMHISensor(SmhiWeatherBaseEntity, SensorEntity):
|
||||
"""Refresh the entity data."""
|
||||
if self.coordinator.data.daily:
|
||||
self._attr_native_value = self.entity_description.value_fn(self)
|
||||
|
||||
|
||||
class SMHIFireSensor(SmhiFireEntity, SensorEntity):
|
||||
"""Representation of a SMHI Weather Sensor."""
|
||||
|
||||
entity_description: SMHIFireEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
latitude: str,
|
||||
longitude: str,
|
||||
coordinator: SMHIFireDataUpdateCoordinator,
|
||||
entity_description: SMHIFireEntityDescription,
|
||||
) -> None:
|
||||
"""Initiate SMHI Sensor."""
|
||||
self.entity_description = entity_description
|
||||
super().__init__(
|
||||
latitude,
|
||||
longitude,
|
||||
coordinator,
|
||||
)
|
||||
self._attr_unique_id = f"{latitude}, {longitude}-{entity_description.key}"
|
||||
|
||||
def update_entity_data(self) -> None:
|
||||
"""Refresh the entity data."""
|
||||
if self.coordinator.data.fire_daily:
|
||||
self._attr_native_value = self.entity_description.value_fn(self)
|
||||
|
||||
@@ -26,12 +26,66 @@
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"build_up_index": {
|
||||
"name": "Build Up Index"
|
||||
},
|
||||
"drought_code": {
|
||||
"name": "Drought Code"
|
||||
},
|
||||
"duff_moisture_code": {
|
||||
"name": "Duff Moisture Code"
|
||||
},
|
||||
"fine_fuel_moisture_code": {
|
||||
"name": "Fine Fuel Moisture Code"
|
||||
},
|
||||
"fire_weather_index": {
|
||||
"name": "Fire Weather Index"
|
||||
},
|
||||
"forestdry": {
|
||||
"name": "Fuel drying",
|
||||
"state": {
|
||||
"dry": "Dry",
|
||||
"extremely_dry": "Extemely dry",
|
||||
"moderate_wet": "Moderate wet",
|
||||
"very_dry": "Very dry",
|
||||
"very_wet": "Very wet",
|
||||
"wet": "Wet"
|
||||
}
|
||||
},
|
||||
"frozen_precipitation": {
|
||||
"name": "Frozen precipitation"
|
||||
},
|
||||
"fwi": {
|
||||
"name": "Fire Weather Index"
|
||||
},
|
||||
"fwiindex": {
|
||||
"name": "FWI-index",
|
||||
"state": {
|
||||
"extreme": "Extremely high risk",
|
||||
"high": "High risk",
|
||||
"low": "Low risk",
|
||||
"moderate": "Moderate risk",
|
||||
"very_high": "Very high risk",
|
||||
"very_low": "Very low risk"
|
||||
}
|
||||
},
|
||||
"grassfire": {
|
||||
"name": "Highest grass fire risk",
|
||||
"state": {
|
||||
"high": "High",
|
||||
"low": "Low",
|
||||
"moderate": "Moderate",
|
||||
"season_over": "Grass fire season over",
|
||||
"snow_cover": "Snow cover",
|
||||
"very_high": "Very high"
|
||||
}
|
||||
},
|
||||
"high_cloud": {
|
||||
"name": "High cloud coverage"
|
||||
},
|
||||
"initial_spread_index": {
|
||||
"name": "Initial Spread Index"
|
||||
},
|
||||
"low_cloud": {
|
||||
"name": "Low cloud coverage"
|
||||
},
|
||||
@@ -50,6 +104,9 @@
|
||||
"6": "Freezing drizzle"
|
||||
}
|
||||
},
|
||||
"rate_of_spread": {
|
||||
"name": "Potential rate of spread"
|
||||
},
|
||||
"thunder": {
|
||||
"name": "Thunder probability"
|
||||
},
|
||||
|
||||
@@ -55,7 +55,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import ATTR_SMHI_THUNDER_PROBABILITY, ENTITY_ID_SENSOR_FORMAT
|
||||
from .coordinator import SMHIConfigEntry
|
||||
from .entity import SmhiWeatherBaseEntity
|
||||
from .entity import SmhiWeatherEntity
|
||||
|
||||
# Used to map condition from API results
|
||||
CONDITION_CLASSES: Final[dict[str, list[int]]] = {
|
||||
@@ -89,7 +89,7 @@ async def async_setup_entry(
|
||||
"""Add a weather entity from map location."""
|
||||
location = config_entry.data
|
||||
|
||||
coordinator = config_entry.runtime_data
|
||||
coordinator = config_entry.runtime_data[0]
|
||||
|
||||
entity = SmhiWeather(
|
||||
location[CONF_LOCATION][CONF_LATITUDE],
|
||||
@@ -101,7 +101,7 @@ async def async_setup_entry(
|
||||
async_add_entities([entity])
|
||||
|
||||
|
||||
class SmhiWeather(SmhiWeatherBaseEntity, SingleCoordinatorWeatherEntity):
|
||||
class SmhiWeather(SmhiWeatherEntity, SingleCoordinatorWeatherEntity):
|
||||
"""Representation of a weather entity."""
|
||||
|
||||
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
|
||||
@@ -7,6 +7,8 @@ import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
from pysmhi.smhi_fire_forecast import SMHIFireForecast, SMHIFirePointForecast
|
||||
from pysmhi.smhi_forecast import SMHIForecast, SMHIPointForecast
|
||||
import pytest
|
||||
|
||||
@@ -40,6 +42,7 @@ async def patch_platform_constant() -> list[Platform]:
|
||||
async def load_int(
|
||||
hass: HomeAssistant,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
load_platforms: list[Platform],
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the SMHI integration."""
|
||||
@@ -87,6 +90,23 @@ async def get_client(
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_fire_client")
|
||||
async def get_fire_client(
|
||||
hass: HomeAssistant,
|
||||
get_fire_data: tuple[list[SMHIFireForecast], list[SMHIFireForecast]],
|
||||
) -> AsyncGenerator[MagicMock]:
|
||||
"""Mock SMHIFirePointForecast client."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.smhi.coordinator.SMHIFirePointForecast",
|
||||
autospec=True,
|
||||
) as mock_client:
|
||||
client = mock_client.return_value
|
||||
client.async_get_daily_forecast.return_value = get_fire_data[0]
|
||||
client.async_get_hourly_forecast.return_value = get_fire_data[1]
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture(name="get_data")
|
||||
async def get_data_from_library(
|
||||
hass: HomeAssistant,
|
||||
@@ -112,9 +132,44 @@ async def get_data_from_library(
|
||||
await client._api._session.close()
|
||||
|
||||
|
||||
@pytest.fixture(name="get_fire_data")
|
||||
async def get_fire_data_from_library(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
load_fire_json: dict[str, Any],
|
||||
) -> AsyncGenerator[tuple[list[SMHIFireForecast], list[SMHIFireForecast]]]:
|
||||
"""Get data from api."""
|
||||
client = SMHIFirePointForecast(
|
||||
TEST_CONFIG[CONF_LOCATION][CONF_LONGITUDE],
|
||||
TEST_CONFIG[CONF_LOCATION][CONF_LATITUDE],
|
||||
aioclient_mock.create_session(hass.loop),
|
||||
)
|
||||
with (
|
||||
freeze_time("2025-10-03"),
|
||||
patch.object(
|
||||
client._api,
|
||||
"async_get_data",
|
||||
return_value=load_fire_json,
|
||||
),
|
||||
):
|
||||
data_daily = await client.async_get_daily_forecast()
|
||||
data_hourly = await client.async_get_hourly_forecast()
|
||||
|
||||
yield (data_daily, data_hourly)
|
||||
await client._api._session.close()
|
||||
|
||||
|
||||
@pytest.fixture(name="load_fire_json")
|
||||
def load_fire_json_from_fixture(
|
||||
load_data: tuple[str, str, str, str],
|
||||
) -> dict[str, Any]:
|
||||
"""Load fixture with json data and return."""
|
||||
return json.loads(load_data[3])
|
||||
|
||||
|
||||
@pytest.fixture(name="load_json")
|
||||
def load_json_from_fixture(
|
||||
load_data: tuple[str, str, str],
|
||||
load_data: tuple[str, str, str, str],
|
||||
to_load: int,
|
||||
) -> dict[str, Any]:
|
||||
"""Load fixture with json data and return."""
|
||||
@@ -122,12 +177,13 @@ def load_json_from_fixture(
|
||||
|
||||
|
||||
@pytest.fixture(name="load_data", scope="package")
|
||||
def load_data_from_fixture() -> tuple[str, str, str]:
|
||||
def load_data_from_fixture() -> tuple[str, str, str, str]:
|
||||
"""Load fixture with fixture data and return."""
|
||||
return (
|
||||
load_fixture("smhi.json", "smhi"),
|
||||
load_fixture("smhi_night.json", "smhi"),
|
||||
load_fixture("smhi_short.json", "smhi"),
|
||||
load_fixture("smhi_fire.json", "smhi"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
1365
tests/components/smhi/fixtures/smhi_fire.json
Normal file
1365
tests/components/smhi/fixtures/smhi_fire.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,264 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_build_up_index-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_build_up_index',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Build Up Index',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'build_up_index',
|
||||
'unique_id': '59.32624, 17.84197-build_up_index',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_build_up_index-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Build Up Index',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_build_up_index',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '39.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_drought_code-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_drought_code',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Drought Code',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'drought_code',
|
||||
'unique_id': '59.32624, 17.84197-drought_code',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_drought_code-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Drought Code',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_drought_code',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '508.2',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_duff_moisture_code-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_duff_moisture_code',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Duff Moisture Code',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'duff_moisture_code',
|
||||
'unique_id': '59.32624, 17.84197-duff_moisture_code',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_duff_moisture_code-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Duff Moisture Code',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_duff_moisture_code',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '21.6',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fine_fuel_moisture_code-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_fine_fuel_moisture_code',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Fine Fuel Moisture Code',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'fine_fuel_moisture_code',
|
||||
'unique_id': '59.32624, 17.84197-fine_fuel_moisture_code',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fine_fuel_moisture_code-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Fine Fuel Moisture Code',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_fine_fuel_moisture_code',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '83.2',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fire_weather_index-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_fire_weather_index',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Fire Weather Index',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'fire_weather_index',
|
||||
'unique_id': '59.32624, 17.84197-fire_weather_index',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fire_weather_index-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Fire Weather Index',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_fire_weather_index',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '6.6',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_frozen_precipitation-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -49,6 +309,140 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fuel_drying-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'very_wet',
|
||||
'wet',
|
||||
'moderate_wet',
|
||||
'dry',
|
||||
'very_dry',
|
||||
'extremely_dry',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_fuel_drying',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Fuel drying',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'forestdry',
|
||||
'unique_id': '59.32624, 17.84197-forestdry',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fuel_drying-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Test Fuel drying',
|
||||
'options': list([
|
||||
'very_wet',
|
||||
'wet',
|
||||
'moderate_wet',
|
||||
'dry',
|
||||
'very_dry',
|
||||
'extremely_dry',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_fuel_drying',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'moderate_wet',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fwi_index-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'very_low',
|
||||
'low',
|
||||
'moderate',
|
||||
'high',
|
||||
'very_high',
|
||||
'extreme',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_fwi_index',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'FWI-index',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'fwiindex',
|
||||
'unique_id': '59.32624, 17.84197-fwiindex',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_fwi_index-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Test FWI-index',
|
||||
'options': list([
|
||||
'very_low',
|
||||
'low',
|
||||
'moderate',
|
||||
'high',
|
||||
'very_high',
|
||||
'extreme',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_fwi_index',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'low',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_high_cloud_coverage-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -99,6 +493,125 @@
|
||||
'state': '88',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_highest_grass_fire_risk-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'snow_cover',
|
||||
'season_over',
|
||||
'low',
|
||||
'moderate',
|
||||
'high',
|
||||
'very_high',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_highest_grass_fire_risk',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Highest grass fire risk',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'grassfire',
|
||||
'unique_id': '59.32624, 17.84197-grassfire',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_highest_grass_fire_risk-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Test Highest grass fire risk',
|
||||
'options': list([
|
||||
'snow_cover',
|
||||
'season_over',
|
||||
'low',
|
||||
'moderate',
|
||||
'high',
|
||||
'very_high',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_highest_grass_fire_risk',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_initial_spread_index-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_initial_spread_index',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Initial Spread Index',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'initial_spread_index',
|
||||
'unique_id': '59.32624, 17.84197-initial_spread_index',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_initial_spread_index-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'friendly_name': 'Test Initial Spread Index',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_initial_spread_index',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2.6',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_low_cloud_coverage-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -199,6 +712,63 @@
|
||||
'state': '88',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_potential_rate_of_spread-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_potential_rate_of_spread',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.SPEED: 'speed'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Potential rate of spread',
|
||||
'platform': 'smhi',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'rate_of_spread',
|
||||
'unique_id': '59.32624, 17.84197-rate_of_spread',
|
||||
'unit_of_measurement': <UnitOfSpeed.METERS_PER_MINUTE: 'm/min'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_potential_rate_of_spread-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'device_class': 'speed',
|
||||
'friendly_name': 'Test Potential rate of spread',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfSpeed.METERS_PER_MINUTE: 'm/min'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_potential_rate_of_spread',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_setup[load_platforms0][sensor.test_precipitation_category-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -23,6 +23,7 @@ pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
mock_fire_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test we get the form and create an entry."""
|
||||
|
||||
@@ -87,6 +88,7 @@ async def test_form(
|
||||
async def test_form_invalid_coordinates(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
mock_fire_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test we handle invalid coordinates."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -133,6 +135,7 @@ async def test_form_invalid_coordinates(
|
||||
async def test_form_unique_id_exist(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
mock_fire_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test we handle unique id already exist."""
|
||||
entry = MockConfigEntry(
|
||||
@@ -168,6 +171,7 @@ async def test_form_unique_id_exist(
|
||||
async def test_reconfigure_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
mock_fire_client: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Test SMHI component setup process."""
|
||||
|
||||
from pysmhi import SMHIPointForecast
|
||||
from pysmhi import SMHIFirePointForecast, SMHIPointForecast
|
||||
|
||||
from homeassistant.components.smhi.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
@@ -34,6 +34,7 @@ async def test_migrate_entry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
) -> None:
|
||||
"""Test migrate entry data."""
|
||||
|
||||
@@ -65,7 +66,9 @@ async def test_migrate_entry(
|
||||
|
||||
|
||||
async def test_migrate_from_future_version(
|
||||
hass: HomeAssistant, mock_client: SMHIPointForecast
|
||||
hass: HomeAssistant,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
) -> None:
|
||||
"""Test migrate entry not possible from future version."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=TEST_CONFIG_MIGRATE, version=4)
|
||||
|
||||
@@ -5,7 +5,12 @@ from unittest.mock import MagicMock
|
||||
|
||||
from freezegun import freeze_time
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pysmhi import SMHIForecast, SmhiForecastException, SMHIPointForecast
|
||||
from pysmhi import (
|
||||
SMHIFirePointForecast,
|
||||
SMHIForecast,
|
||||
SmhiForecastException,
|
||||
SMHIPointForecast,
|
||||
)
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -65,6 +70,7 @@ async def test_setup_hass(
|
||||
async def test_clear_night(
|
||||
hass: HomeAssistant,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test for successfully setting up the smhi integration."""
|
||||
@@ -102,6 +108,7 @@ async def test_properties_no_data(
|
||||
hass: HomeAssistant,
|
||||
load_int: MockConfigEntry,
|
||||
mock_client: MagicMock,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test properties when no API data available."""
|
||||
@@ -135,7 +142,8 @@ async def test_properties_no_data(
|
||||
|
||||
async def test_properties_unknown_symbol(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
) -> None:
|
||||
"""Test behaviour when unknown symbol from API."""
|
||||
data = SMHIForecast(
|
||||
@@ -244,7 +252,8 @@ async def test_refresh_weather_forecast_retry(
|
||||
hass: HomeAssistant,
|
||||
error: Exception,
|
||||
load_int: MockConfigEntry,
|
||||
mock_client: MagicMock,
|
||||
mock_client: SMHIPointForecast,
|
||||
mock_fire_client: SMHIFirePointForecast,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the refresh weather forecast function."""
|
||||
|
||||
Reference in New Issue
Block a user