mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Use NamedTuple - nws (#53293)
This commit is contained in:
parent
e78a62c802
commit
d98e580c3c
@ -1,5 +1,8 @@
|
||||
"""Constants for National Weather Service Integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import NamedTuple
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
@ -17,7 +20,6 @@ from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_WINDY_VARIANT,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
DEGREE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
@ -40,11 +42,6 @@ ATTRIBUTION = "Data from National Weather Service/NOAA"
|
||||
|
||||
ATTR_FORECAST_DETAILED_DESCRIPTION = "detailed_description"
|
||||
ATTR_FORECAST_DAYTIME = "daytime"
|
||||
ATTR_ICON = "icon"
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT = "unit"
|
||||
ATTR_UNIT_CONVERT = "unit_convert"
|
||||
ATTR_UNIT_CONVERT_METHOD = "unit_convert_method"
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
ATTR_CONDITION_EXCEPTIONAL: [
|
||||
@ -101,82 +98,93 @@ COORDINATOR_FORECAST_HOURLY = "coordinator_forecast_hourly"
|
||||
OBSERVATION_VALID_TIME = timedelta(minutes=20)
|
||||
FORECAST_VALID_TIME = timedelta(minutes=45)
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"dewpoint": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Dew Point",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"temperature": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Temperature",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"windChill": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Wind Chill",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"heatIndex": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Heat Index",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"relativeHumidity": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Relative Humidity",
|
||||
ATTR_UNIT: PERCENTAGE,
|
||||
ATTR_UNIT_CONVERT: PERCENTAGE,
|
||||
},
|
||||
"windSpeed": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-windy",
|
||||
ATTR_LABEL: "Wind Speed",
|
||||
ATTR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
ATTR_UNIT_CONVERT: SPEED_MILES_PER_HOUR,
|
||||
},
|
||||
"windGust": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-windy",
|
||||
ATTR_LABEL: "Wind Gust",
|
||||
ATTR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
ATTR_UNIT_CONVERT: SPEED_MILES_PER_HOUR,
|
||||
},
|
||||
"windDirection": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:compass-rose",
|
||||
ATTR_LABEL: "Wind Direction",
|
||||
ATTR_UNIT: DEGREE,
|
||||
ATTR_UNIT_CONVERT: DEGREE,
|
||||
},
|
||||
"barometricPressure": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Barometric Pressure",
|
||||
ATTR_UNIT: PRESSURE_PA,
|
||||
ATTR_UNIT_CONVERT: PRESSURE_INHG,
|
||||
},
|
||||
"seaLevelPressure": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Sea Level Pressure",
|
||||
ATTR_UNIT: PRESSURE_PA,
|
||||
ATTR_UNIT_CONVERT: PRESSURE_INHG,
|
||||
},
|
||||
"visibility": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:eye",
|
||||
ATTR_LABEL: "Visibility",
|
||||
ATTR_UNIT: LENGTH_METERS,
|
||||
ATTR_UNIT_CONVERT: LENGTH_MILES,
|
||||
},
|
||||
|
||||
class NWSSensorMetadata(NamedTuple):
|
||||
"""Sensor metadata for an individual NWS sensor."""
|
||||
|
||||
label: str
|
||||
icon: str | None
|
||||
device_class: str | None
|
||||
unit: str
|
||||
unit_convert: str
|
||||
|
||||
|
||||
SENSOR_TYPES: dict[str, NWSSensorMetadata] = {
|
||||
"dewpoint": NWSSensorMetadata(
|
||||
"Dew Point",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"temperature": NWSSensorMetadata(
|
||||
"Temperature",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"windChill": NWSSensorMetadata(
|
||||
"Wind Chill",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"heatIndex": NWSSensorMetadata(
|
||||
"Heat Index",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"relativeHumidity": NWSSensorMetadata(
|
||||
"Relative Humidity",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
unit=PERCENTAGE,
|
||||
unit_convert=PERCENTAGE,
|
||||
),
|
||||
"windSpeed": NWSSensorMetadata(
|
||||
"Wind Speed",
|
||||
icon="mdi:weather-windy",
|
||||
device_class=None,
|
||||
unit=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_convert=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
"windGust": NWSSensorMetadata(
|
||||
"Wind Gust",
|
||||
icon="mdi:weather-windy",
|
||||
device_class=None,
|
||||
unit=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_convert=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
"windDirection": NWSSensorMetadata(
|
||||
"Wind Direction",
|
||||
icon="mdi:compass-rose",
|
||||
device_class=None,
|
||||
unit=DEGREE,
|
||||
unit_convert=DEGREE,
|
||||
),
|
||||
"barometricPressure": NWSSensorMetadata(
|
||||
"Barometric Pressure",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
unit=PRESSURE_PA,
|
||||
unit_convert=PRESSURE_INHG,
|
||||
),
|
||||
"seaLevelPressure": NWSSensorMetadata(
|
||||
"Sea Level Pressure",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
unit=PRESSURE_PA,
|
||||
unit_convert=PRESSURE_INHG,
|
||||
),
|
||||
"visibility": NWSSensorMetadata(
|
||||
"Visibility",
|
||||
icon="mdi:eye",
|
||||
device_class=None,
|
||||
unit=LENGTH_METERS,
|
||||
unit_convert=LENGTH_MILES,
|
||||
),
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_DEVICE_CLASS,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
LENGTH_KILOMETERS,
|
||||
@ -14,6 +13,7 @@ from homeassistant.const import (
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.distance import convert as convert_distance
|
||||
from homeassistant.util.dt import utcnow
|
||||
@ -21,10 +21,6 @@ from homeassistant.util.pressure import convert as convert_pressure
|
||||
|
||||
from . import base_unique_id
|
||||
from .const import (
|
||||
ATTR_ICON,
|
||||
ATTR_LABEL,
|
||||
ATTR_UNIT,
|
||||
ATTR_UNIT_CONVERT,
|
||||
ATTRIBUTION,
|
||||
CONF_STATION,
|
||||
COORDINATOR_OBSERVATION,
|
||||
@ -32,6 +28,7 @@ from .const import (
|
||||
NWS_DATA,
|
||||
OBSERVATION_VALID_TIME,
|
||||
SENSOR_TYPES,
|
||||
NWSSensorMetadata,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
@ -43,21 +40,15 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
||||
station = entry.data[CONF_STATION]
|
||||
|
||||
entities = []
|
||||
for sensor_type, sensor_data in SENSOR_TYPES.items():
|
||||
if hass.config.units.is_metric:
|
||||
unit = sensor_data[ATTR_UNIT]
|
||||
else:
|
||||
unit = sensor_data[ATTR_UNIT_CONVERT]
|
||||
for sensor_type, metadata in SENSOR_TYPES.items():
|
||||
entities.append(
|
||||
NWSSensor(
|
||||
hass,
|
||||
entry.data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
metadata,
|
||||
station,
|
||||
sensor_data[ATTR_LABEL],
|
||||
sensor_data[ATTR_ICON],
|
||||
sensor_data[ATTR_DEVICE_CLASS],
|
||||
unit,
|
||||
),
|
||||
)
|
||||
|
||||
@ -69,14 +60,12 @@ class NWSSensor(CoordinatorEntity, SensorEntity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry_data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
metadata: NWSSensorMetadata,
|
||||
station,
|
||||
label,
|
||||
icon,
|
||||
device_class,
|
||||
unit,
|
||||
):
|
||||
"""Initialise the platform with a data instance."""
|
||||
super().__init__(hass_data[COORDINATOR_OBSERVATION])
|
||||
@ -84,11 +73,15 @@ class NWSSensor(CoordinatorEntity, SensorEntity):
|
||||
self._latitude = entry_data[CONF_LATITUDE]
|
||||
self._longitude = entry_data[CONF_LONGITUDE]
|
||||
self._type = sensor_type
|
||||
self._station = station
|
||||
self._label = label
|
||||
self._icon = icon
|
||||
self._device_class = device_class
|
||||
self._unit = unit
|
||||
self._metadata = metadata
|
||||
|
||||
self._attr_name = f"{station} {metadata.label}"
|
||||
self._attr_icon = metadata.icon
|
||||
self._attr_device_class = metadata.device_class
|
||||
if hass.config.units.is_metric:
|
||||
self._attr_unit_of_measurement = metadata.unit
|
||||
else:
|
||||
self._attr_unit_of_measurement = metadata.unit_convert
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
@ -96,43 +89,23 @@ class NWSSensor(CoordinatorEntity, SensorEntity):
|
||||
value = self._nws.observation.get(self._type)
|
||||
if value is None:
|
||||
return None
|
||||
if self._unit == SPEED_MILES_PER_HOUR:
|
||||
if self._attr_unit_of_measurement == SPEED_MILES_PER_HOUR:
|
||||
return round(convert_distance(value, LENGTH_KILOMETERS, LENGTH_MILES))
|
||||
if self._unit == LENGTH_MILES:
|
||||
if self._attr_unit_of_measurement == LENGTH_MILES:
|
||||
return round(convert_distance(value, LENGTH_METERS, LENGTH_MILES))
|
||||
if self._unit == PRESSURE_INHG:
|
||||
if self._attr_unit_of_measurement == PRESSURE_INHG:
|
||||
return round(convert_pressure(value, PRESSURE_PA, PRESSURE_INHG), 2)
|
||||
if self._unit == TEMP_CELSIUS:
|
||||
if self._attr_unit_of_measurement == TEMP_CELSIUS:
|
||||
return round(value, 1)
|
||||
if self._unit == PERCENTAGE:
|
||||
if self._attr_unit_of_measurement == PERCENTAGE:
|
||||
return round(value)
|
||||
return value
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit the value is expressed in."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the attribution."""
|
||||
return {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the station."""
|
||||
return f"{self._station} {self._label}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
|
@ -1,12 +1,7 @@
|
||||
"""Sensors for National Weather Service (NWS)."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.nws.const import (
|
||||
ATTR_LABEL,
|
||||
ATTRIBUTION,
|
||||
DOMAIN,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
from homeassistant.components.nws.const import ATTRIBUTION, DOMAIN, SENSOR_TYPES
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN
|
||||
from homeassistant.util import slugify
|
||||
@ -40,12 +35,12 @@ async def test_imperial_metric(
|
||||
"""Test with imperial and metric units."""
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{sensor_data[ATTR_LABEL]}",
|
||||
suggested_object_id=f"abc_{metadata.label}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
@ -58,8 +53,8 @@ async def test_imperial_metric(
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(sensor_data[ATTR_LABEL])}")
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(metadata.label)}")
|
||||
assert state
|
||||
assert state.state == result_observation[sensor_name]
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
@ -72,12 +67,12 @@ async def test_none_values(hass, mock_simple_nws, no_weather):
|
||||
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{sensor_data[ATTR_LABEL]}",
|
||||
suggested_object_id=f"abc_{metadata.label}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
@ -89,7 +84,7 @@ async def test_none_values(hass, mock_simple_nws, no_weather):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(sensor_data[ATTR_LABEL])}")
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(metadata.label)}")
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
Loading…
x
Reference in New Issue
Block a user