ha-core/homeassistant/components/norway_air/air_quality.py

147 lines
4.3 KiB
Python

"""Sensor for checking the air quality forecast around Norway."""
from __future__ import annotations
from datetime import timedelta
import logging
import metno
import voluptuous as vol
from homeassistant.components.air_quality import PLATFORM_SCHEMA, AirQualityEntity
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = (
"Air quality from "
"https://luftkvalitet.miljostatus.no/, "
"delivered by the Norwegian Meteorological Institute."
)
# https://api.met.no/license_data.html
CONF_FORECAST = "forecast"
DEFAULT_FORECAST = 0
DEFAULT_NAME = "Air quality Norway"
OVERRIDE_URL = "https://aa015h6buqvih86i1.api.met.no/weatherapi/airqualityforecast/0.1/"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_FORECAST, default=DEFAULT_FORECAST): vol.Coerce(int),
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
SCAN_INTERVAL = timedelta(minutes=5)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the air_quality norway sensor."""
forecast = config.get(CONF_FORECAST)
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
name = config.get(CONF_NAME)
if None in (latitude, longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return
coordinates = {"lat": str(latitude), "lon": str(longitude)}
async_add_entities(
[AirSensor(name, coordinates, forecast, async_get_clientsession(hass))], True
)
def round_state(func):
"""Round state."""
def _decorator(self):
res = func(self)
if isinstance(res, float):
return round(res, 2)
return res
return _decorator
class AirSensor(AirQualityEntity):
"""Representation of an air quality sensor."""
def __init__(self, name, coordinates, forecast, session):
"""Initialize the sensor."""
self._name = name
self._api = metno.AirQualityData(
coordinates, forecast, session, api_url=OVERRIDE_URL
)
@property
def attribution(self) -> str:
"""Return the attribution."""
return ATTRIBUTION
@property
def extra_state_attributes(self) -> dict:
"""Return other details about the sensor state."""
return {
"level": self._api.data.get("level"),
"location": self._api.data.get("location"),
}
@property
def name(self) -> str:
"""Return the name of the sensor."""
return self._name
@property # type: ignore[misc]
@round_state
def air_quality_index(self):
"""Return the Air Quality Index (AQI)."""
return self._api.data.get("aqi")
@property # type: ignore[misc]
@round_state
def nitrogen_dioxide(self):
"""Return the NO2 (nitrogen dioxide) level."""
return self._api.data.get("no2_concentration")
@property # type: ignore[misc]
@round_state
def ozone(self):
"""Return the O3 (ozone) level."""
return self._api.data.get("o3_concentration")
@property # type: ignore[misc]
@round_state
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
return self._api.data.get("pm25_concentration")
@property # type: ignore[misc]
@round_state
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return self._api.data.get("pm10_concentration")
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._api.units.get("pm25_concentration")
async def async_update(self) -> None:
"""Update the sensor."""
await self._api.update()