mirror of
https://github.com/home-assistant/core.git
synced 2025-08-07 06:35:10 +02:00
Precommit black missed a file?
This commit is contained in:
@@ -8,13 +8,29 @@ import aiohttp
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
WeatherEntity, PLATFORM_SCHEMA, ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_TEMP, ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_SPEED, ATTR_FORECAST_WIND_BEARING)
|
||||
WeatherEntity,
|
||||
PLATFORM_SCHEMA,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_TEMP,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_SPEED,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY, CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE,
|
||||
LENGTH_KILOMETERS, LENGTH_METERS, LENGTH_MILES, PRESSURE_HPA, PRESSURE_PA,
|
||||
PRESSURE_INHG, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
CONF_API_KEY,
|
||||
CONF_NAME,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MODE,
|
||||
LENGTH_KILOMETERS,
|
||||
LENGTH_METERS,
|
||||
LENGTH_MILES,
|
||||
PRESSURE_HPA,
|
||||
PRESSURE_PA,
|
||||
PRESSURE_INHG,
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.exceptions import PlatformNotReady, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -25,62 +41,81 @@ from homeassistant.util.temperature import convert as convert_temperature
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTRIBUTION = 'Data from National Weather Service/NOAA'
|
||||
ATTRIBUTION = "Data from National Weather Service/NOAA"
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
|
||||
|
||||
CONF_STATION = 'station'
|
||||
CONF_STATION = "station"
|
||||
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION = 'detailed_description'
|
||||
ATTR_FORECAST_PRECIP_PROB = 'precipitation_probability'
|
||||
ATTR_FORECAST_DAYTIME = 'daytime'
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION = "detailed_description"
|
||||
ATTR_FORECAST_PRECIP_PROB = "precipitation_probability"
|
||||
ATTR_FORECAST_DAYTIME = "daytime"
|
||||
|
||||
# Ordered so that a single condition can be chosen from multiple weather codes.
|
||||
# Catalog of NWS icon weather codes listed at:
|
||||
# https://api.weather.gov/icons
|
||||
CONDITION_CLASSES = OrderedDict([
|
||||
('snowy', ['Snow',
|
||||
'Sleet',
|
||||
'Blizzard']),
|
||||
('snowy-rainy', ['Rain/snow',
|
||||
'Rain/sleet',
|
||||
'Freezing rain/snow',
|
||||
'Freezing rain',
|
||||
'Rain/freezing rain']),
|
||||
('hail', []),
|
||||
('lightning-rainy', ['Thunderstorm (high cloud cover)',
|
||||
'Thunderstorm (medium cloud cover)',
|
||||
'Thunderstorm (low cloud cover)']),
|
||||
('lightning', []),
|
||||
('pouring', []),
|
||||
('rainy', ['Rain',
|
||||
'Rain showers (high cloud cover)',
|
||||
'Rain showers (low cloud cover)']),
|
||||
('windy-variant', ['Mostly cloudy and windy',
|
||||
'Overcast and windy']),
|
||||
('windy', ['Fair/clear and windy',
|
||||
'A few clouds and windy',
|
||||
'Partly cloudy and windy']),
|
||||
('fog', ['Fog/mist']),
|
||||
('clear', ['Fair/clear']), # sunny and clear-night
|
||||
('cloudy', ['Mostly cloudy',
|
||||
'Overcast']),
|
||||
('partlycloudy', ['A few clouds',
|
||||
'Partly cloudy']),
|
||||
])
|
||||
CONDITION_CLASSES = OrderedDict(
|
||||
[
|
||||
("snowy", ["Snow", "Sleet", "Blizzard"]),
|
||||
(
|
||||
"snowy-rainy",
|
||||
[
|
||||
"Rain/snow",
|
||||
"Rain/sleet",
|
||||
"Freezing rain/snow",
|
||||
"Freezing rain",
|
||||
"Rain/freezing rain",
|
||||
],
|
||||
),
|
||||
("hail", []),
|
||||
(
|
||||
"lightning-rainy",
|
||||
[
|
||||
"Thunderstorm (high cloud cover)",
|
||||
"Thunderstorm (medium cloud cover)",
|
||||
"Thunderstorm (low cloud cover)",
|
||||
],
|
||||
),
|
||||
("lightning", []),
|
||||
("pouring", []),
|
||||
(
|
||||
"rainy",
|
||||
[
|
||||
"Rain",
|
||||
"Rain showers (high cloud cover)",
|
||||
"Rain showers (low cloud cover)",
|
||||
],
|
||||
),
|
||||
("windy-variant", ["Mostly cloudy and windy", "Overcast and windy"]),
|
||||
(
|
||||
"windy",
|
||||
[
|
||||
"Fair/clear and windy",
|
||||
"A few clouds and windy",
|
||||
"Partly cloudy and windy",
|
||||
],
|
||||
),
|
||||
("fog", ["Fog/mist"]),
|
||||
("clear", ["Fair/clear"]), # sunny and clear-night
|
||||
("cloudy", ["Mostly cloudy", "Overcast"]),
|
||||
("partlycloudy", ["A few clouds", "Partly cloudy"]),
|
||||
]
|
||||
)
|
||||
|
||||
ERRORS = (aiohttp.ClientError, JSONDecodeError)
|
||||
|
||||
FORECAST_MODE = ['daynight', 'hourly']
|
||||
FORECAST_MODE = ["daynight", "hourly"]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_LATITUDE): cv.latitude,
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(CONF_MODE, default='daynight'): vol.In(FORECAST_MODE),
|
||||
vol.Optional(CONF_STATION): cv.string,
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
})
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_LATITUDE): cv.latitude,
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(CONF_MODE, default="daynight"): vol.In(FORECAST_MODE),
|
||||
vol.Optional(CONF_STATION): cv.string,
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def convert_condition(time, weather):
|
||||
@@ -94,22 +129,27 @@ def convert_condition(time, weather):
|
||||
prec_probs = [w[1] or 0 for w in weather]
|
||||
|
||||
# Choose condition with highest priority.
|
||||
cond = next((key for key, value in CONDITION_CLASSES.items()
|
||||
if any(condition in value for condition in conditions)),
|
||||
conditions[0])
|
||||
cond = next(
|
||||
(
|
||||
key
|
||||
for key, value in CONDITION_CLASSES.items()
|
||||
if any(condition in value for condition in conditions)
|
||||
),
|
||||
conditions[0],
|
||||
)
|
||||
|
||||
if cond == 'clear':
|
||||
if time == 'day':
|
||||
return 'sunny', max(prec_probs)
|
||||
if time == 'night':
|
||||
return 'clear-night', max(prec_probs)
|
||||
if cond == "clear":
|
||||
if time == "day":
|
||||
return "sunny", max(prec_probs)
|
||||
if time == "night":
|
||||
return "clear-night", max(prec_probs)
|
||||
return cond, max(prec_probs)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the NWS weather platform."""
|
||||
from pynws import SimpleNWS
|
||||
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
station = config.get(CONF_STATION)
|
||||
@@ -129,17 +169,20 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||
try:
|
||||
await nws.set_station(station)
|
||||
except ERRORS as status:
|
||||
_LOGGER.error("Error getting station list for %s: %s",
|
||||
(latitude, longitude), status)
|
||||
_LOGGER.error(
|
||||
"Error getting station list for %s: %s", (latitude, longitude), status
|
||||
)
|
||||
raise PlatformNotReady
|
||||
|
||||
_LOGGER.debug("Station list: %s", nws.stations)
|
||||
_LOGGER.debug("Initialized for coordinates %s, %s -> station %s",
|
||||
latitude, longitude, nws.station)
|
||||
_LOGGER.debug(
|
||||
"Initialized for coordinates %s, %s -> station %s",
|
||||
latitude,
|
||||
longitude,
|
||||
nws.station,
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
[NWSWeather(nws, mode, hass.config.units, config)],
|
||||
True)
|
||||
async_add_entities([NWSWeather(nws, mode, hass.config.units, config)], True)
|
||||
|
||||
|
||||
class NWSWeather(WeatherEntity):
|
||||
@@ -162,16 +205,20 @@ class NWSWeather(WeatherEntity):
|
||||
try:
|
||||
await self.nws.update_observation()
|
||||
except ERRORS as status:
|
||||
_LOGGER.error("Error updating observation from station %s: %s",
|
||||
self.nws.station, status)
|
||||
_LOGGER.error(
|
||||
"Error updating observation from station %s: %s",
|
||||
self.nws.station,
|
||||
status,
|
||||
)
|
||||
else:
|
||||
self.observation = self.nws.observation
|
||||
_LOGGER.debug("Updating forecast")
|
||||
try:
|
||||
await self.nws.update_forecast()
|
||||
except ERRORS as status:
|
||||
_LOGGER.error("Error updating forecast from station %s: %s",
|
||||
self.nws.station, status)
|
||||
_LOGGER.error(
|
||||
"Error updating forecast from station %s: %s", self.nws.station, status
|
||||
)
|
||||
else:
|
||||
self._forecast = self.nws.forecast
|
||||
return
|
||||
@@ -191,7 +238,7 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return the current temperature."""
|
||||
temp_c = None
|
||||
if self.observation:
|
||||
temp_c = self.observation.get('temperature')
|
||||
temp_c = self.observation.get("temperature")
|
||||
if temp_c:
|
||||
return convert_temperature(temp_c, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
return None
|
||||
@@ -201,16 +248,14 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return the current pressure."""
|
||||
pressure_pa = None
|
||||
if self.observation:
|
||||
pressure_pa = self.observation.get('seaLevelPressure')
|
||||
pressure_pa = self.observation.get("seaLevelPressure")
|
||||
if pressure_pa is None:
|
||||
return None
|
||||
if self.is_metric:
|
||||
pressure = convert_pressure(pressure_pa,
|
||||
PRESSURE_PA, PRESSURE_HPA)
|
||||
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_HPA)
|
||||
pressure = round(pressure)
|
||||
else:
|
||||
pressure = convert_pressure(pressure_pa,
|
||||
PRESSURE_PA, PRESSURE_INHG)
|
||||
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_INHG)
|
||||
pressure = round(pressure, 2)
|
||||
return pressure
|
||||
|
||||
@@ -219,7 +264,7 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return the name of the sensor."""
|
||||
humidity = None
|
||||
if self.observation:
|
||||
humidity = self.observation.get('relativeHumidity')
|
||||
humidity = self.observation.get("relativeHumidity")
|
||||
return humidity
|
||||
|
||||
@property
|
||||
@@ -227,14 +272,13 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return the current windspeed."""
|
||||
wind_m_s = None
|
||||
if self.observation:
|
||||
wind_m_s = self.observation.get('windSpeed')
|
||||
wind_m_s = self.observation.get("windSpeed")
|
||||
if wind_m_s is None:
|
||||
return None
|
||||
wind_m_hr = wind_m_s * 3600
|
||||
|
||||
if self.is_metric:
|
||||
wind = convert_distance(wind_m_hr,
|
||||
LENGTH_METERS, LENGTH_KILOMETERS)
|
||||
wind = convert_distance(wind_m_hr, LENGTH_METERS, LENGTH_KILOMETERS)
|
||||
else:
|
||||
wind = convert_distance(wind_m_hr, LENGTH_METERS, LENGTH_MILES)
|
||||
return round(wind)
|
||||
@@ -244,7 +288,7 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return the current wind bearing (degrees)."""
|
||||
wind_bearing = None
|
||||
if self.observation:
|
||||
wind_bearing = self.observation.get('windDirection')
|
||||
wind_bearing = self.observation.get("windDirection")
|
||||
return wind_bearing
|
||||
|
||||
@property
|
||||
@@ -257,8 +301,8 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return current condition."""
|
||||
weather = None
|
||||
if self.observation:
|
||||
weather = self.observation.get('iconWeather')
|
||||
time = self.observation.get('iconTime')
|
||||
weather = self.observation.get("iconWeather")
|
||||
time = self.observation.get("iconTime")
|
||||
|
||||
if weather:
|
||||
cond, _ = convert_condition(time, weather)
|
||||
@@ -270,7 +314,7 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return visibility."""
|
||||
vis_m = None
|
||||
if self.observation:
|
||||
vis_m = self.observation.get('visibility')
|
||||
vis_m = self.observation.get("visibility")
|
||||
if vis_m is None:
|
||||
return None
|
||||
|
||||
@@ -289,15 +333,16 @@ class NWSWeather(WeatherEntity):
|
||||
for forecast_entry in self._forecast:
|
||||
data = {
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION: forecast_entry.get(
|
||||
'detailedForecast'),
|
||||
ATTR_FORECAST_TEMP: forecast_entry.get('temperature'),
|
||||
ATTR_FORECAST_TIME: forecast_entry.get('startTime'),
|
||||
"detailedForecast"
|
||||
),
|
||||
ATTR_FORECAST_TEMP: forecast_entry.get("temperature"),
|
||||
ATTR_FORECAST_TIME: forecast_entry.get("startTime"),
|
||||
}
|
||||
|
||||
if self.mode == 'daynight':
|
||||
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get('isDaytime')
|
||||
time = forecast_entry.get('iconTime')
|
||||
weather = forecast_entry.get('iconWeather')
|
||||
if self.mode == "daynight":
|
||||
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get("isDaytime")
|
||||
time = forecast_entry.get("iconTime")
|
||||
weather = forecast_entry.get("iconWeather")
|
||||
if time and weather:
|
||||
cond, precip = convert_condition(time, weather)
|
||||
else:
|
||||
@@ -305,14 +350,13 @@ class NWSWeather(WeatherEntity):
|
||||
data[ATTR_FORECAST_CONDITION] = cond
|
||||
data[ATTR_FORECAST_PRECIP_PROB] = precip
|
||||
|
||||
data[ATTR_FORECAST_WIND_BEARING] = \
|
||||
forecast_entry.get('windBearing')
|
||||
wind_speed = forecast_entry.get('windSpeedAvg')
|
||||
data[ATTR_FORECAST_WIND_BEARING] = forecast_entry.get("windBearing")
|
||||
wind_speed = forecast_entry.get("windSpeedAvg")
|
||||
if wind_speed:
|
||||
if self.is_metric:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(
|
||||
convert_distance(wind_speed,
|
||||
LENGTH_MILES, LENGTH_KILOMETERS))
|
||||
convert_distance(wind_speed, LENGTH_MILES, LENGTH_KILOMETERS)
|
||||
)
|
||||
else:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(wind_speed)
|
||||
else:
|
||||
|
Reference in New Issue
Block a user