diff --git a/homeassistant/components/wunderground/__init__.py b/homeassistant/components/wunderground/__init__.py deleted file mode 100644 index faed41fdbea..00000000000 --- a/homeassistant/components/wunderground/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The wunderground component.""" diff --git a/homeassistant/components/wunderground/manifest.json b/homeassistant/components/wunderground/manifest.json deleted file mode 100644 index b932d9ac403..00000000000 --- a/homeassistant/components/wunderground/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "domain": "wunderground", - "name": "Weather Underground (WUnderground)", - "documentation": "https://www.home-assistant.io/integrations/wunderground", - "codeowners": [], - "iot_class": "cloud_polling" -} diff --git a/homeassistant/components/wunderground/sensor.py b/homeassistant/components/wunderground/sensor.py deleted file mode 100644 index 887e2264a70..00000000000 --- a/homeassistant/components/wunderground/sensor.py +++ /dev/null @@ -1,1282 +0,0 @@ -"""Support for WUnderground weather service.""" -from __future__ import annotations - -import asyncio -from datetime import timedelta -import logging -import re -from typing import Any, Callable - -import aiohttp -import async_timeout -import voluptuous as vol - -from homeassistant.components import sensor -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import ( - ATTR_ATTRIBUTION, - CONF_API_KEY, - CONF_LATITUDE, - CONF_LONGITUDE, - CONF_MONITORED_CONDITIONS, - DEGREE, - IRRADIATION_WATTS_PER_SQUARE_METER, - LENGTH_FEET, - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_MILES, - LENGTH_MILLIMETERS, - PERCENTAGE, - PRESSURE_INHG, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) -from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady -from homeassistant.helpers.aiohttp_client import async_get_clientsession -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType -from homeassistant.util import Throttle - -_RESOURCE = "http://api.wunderground.com/api/{}/{}/{}/q/" -_LOGGER = logging.getLogger(__name__) - -ATTRIBUTION = "Data provided by the WUnderground weather service" - -CONF_PWS_ID = "pws_id" -CONF_LANG = "lang" - -DEFAULT_LANG = "EN" - -MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) - - -# Helper classes for declaring sensor configurations - - -class WUSensorConfig: - """WU Sensor Configuration. - - defines basic HA properties of the weather sensor and - stores callbacks that can parse sensor values out of - the json data received by WU API. - """ - - def __init__( - self, - friendly_name: str | Callable, - feature: str, - value: Callable[[WUndergroundData], Any], - unit_of_measurement: str | None = None, - entity_picture=None, - icon: str = "mdi:gauge", - extra_state_attributes=None, - device_class=None, - ) -> None: - """Initialize sensor configuration. - - :param friendly_name: Friendly name - :param feature: WU feature. See: - https://www.wunderground.com/weather/api/d/docs?d=data/index - :param value: callback that extracts desired value from WUndergroundData object - :param unit_of_measurement: unit of measurement - :param entity_picture: value or callback returning URL of entity picture - :param icon: icon name or URL - :param extra_state_attributes: dictionary of attributes, or callable that returns it - """ - self.friendly_name = friendly_name - self.unit_of_measurement = unit_of_measurement - self.feature = feature - self.value = value - self.entity_picture = entity_picture - self.icon = icon - self.extra_state_attributes = extra_state_attributes or {} - self.device_class = device_class - - -class WUCurrentConditionsSensorConfig(WUSensorConfig): - """Helper for defining sensor configurations for current conditions.""" - - def __init__( - self, - friendly_name: str | Callable, - field: str, - icon: str | None = "mdi:gauge", - unit_of_measurement: str | None = None, - device_class=None, - ) -> None: - """Initialize current conditions sensor configuration. - - :param friendly_name: Friendly name of sensor - :field: Field name in the "current_observation" dictionary. - :icon: icon name or URL, if None sensor will use current weather symbol - :unit_of_measurement: unit of measurement - """ - super().__init__( - friendly_name, - "conditions", - value=lambda wu: wu.data["current_observation"][field], - icon=icon, - unit_of_measurement=unit_of_measurement, - entity_picture=lambda wu: wu.data["current_observation"]["icon_url"] - if icon is None - else None, - extra_state_attributes={ - "date": lambda wu: wu.data["current_observation"]["observation_time"] - }, - device_class=device_class, - ) - - -class WUDailyTextForecastSensorConfig(WUSensorConfig): - """Helper for defining sensor configurations for daily text forecasts.""" - - def __init__( - self, period: int, field: str, unit_of_measurement: str | None = None - ) -> None: - """Initialize daily text forecast sensor configuration. - - :param period: forecast period number - :param field: field name to use as value - :param unit_of_measurement: unit of measurement - """ - super().__init__( - friendly_name=lambda wu: wu.data["forecast"]["txt_forecast"]["forecastday"][ - period - ]["title"], - feature="forecast", - value=lambda wu: wu.data["forecast"]["txt_forecast"]["forecastday"][period][ - field - ], - entity_picture=lambda wu: wu.data["forecast"]["txt_forecast"][ - "forecastday" - ][period]["icon_url"], - unit_of_measurement=unit_of_measurement, - extra_state_attributes={ - "date": lambda wu: wu.data["forecast"]["txt_forecast"]["date"] - }, - ) - - -class WUDailySimpleForecastSensorConfig(WUSensorConfig): - """Helper for defining sensor configurations for daily simpleforecasts.""" - - def __init__( - self, - friendly_name: str, - period: int, - field: str, - wu_unit: str | None = None, - ha_unit: str | None = None, - icon=None, - device_class=None, - ) -> None: - """Initialize daily simple forecast sensor configuration. - - :param friendly_name: friendly_name of the sensor - :param period: forecast period number - :param field: field name to use as value - :param wu_unit: "fahrenheit", "celsius", "degrees" etc. see the example json at: - https://www.wunderground.com/weather/api/d/docs?d=data/forecast&MR=1 - :param ha_unit: corresponding unit in Home Assistant - """ - super().__init__( - friendly_name=friendly_name, - feature="forecast", - value=( - lambda wu: wu.data["forecast"]["simpleforecast"]["forecastday"][period][ - field - ][wu_unit] - ) - if wu_unit - else ( - lambda wu: wu.data["forecast"]["simpleforecast"]["forecastday"][period][ - field - ] - ), - unit_of_measurement=ha_unit, - entity_picture=lambda wu: wu.data["forecast"]["simpleforecast"][ - "forecastday" - ][period]["icon_url"] - if not icon - else None, - icon=icon, - extra_state_attributes={ - "date": lambda wu: wu.data["forecast"]["simpleforecast"]["forecastday"][ - period - ]["date"]["pretty"] - }, - device_class=device_class, - ) - - -class WUHourlyForecastSensorConfig(WUSensorConfig): - """Helper for defining sensor configurations for hourly text forecasts.""" - - def __init__(self, period: int, field: int) -> None: - """Initialize hourly forecast sensor configuration. - - :param period: forecast period number - :param field: field name to use as value - """ - super().__init__( - friendly_name=lambda wu: ( - f"{wu.data['hourly_forecast'][period]['FCTTIME']['weekday_name_abbrev']} " - f"{wu.data['hourly_forecast'][period]['FCTTIME']['civil']}" - ), - feature="hourly", - value=lambda wu: wu.data["hourly_forecast"][period][field], - entity_picture=lambda wu: wu.data["hourly_forecast"][period]["icon_url"], - extra_state_attributes={ - "temp_c": lambda wu: wu.data["hourly_forecast"][period]["temp"][ - "metric" - ], - "temp_f": lambda wu: wu.data["hourly_forecast"][period]["temp"][ - "english" - ], - "dewpoint_c": lambda wu: wu.data["hourly_forecast"][period]["dewpoint"][ - "metric" - ], - "dewpoint_f": lambda wu: wu.data["hourly_forecast"][period]["dewpoint"][ - "english" - ], - "precip_prop": lambda wu: wu.data["hourly_forecast"][period]["pop"], - "sky": lambda wu: wu.data["hourly_forecast"][period]["sky"], - "precip_mm": lambda wu: wu.data["hourly_forecast"][period]["qpf"][ - "metric" - ], - "precip_in": lambda wu: wu.data["hourly_forecast"][period]["qpf"][ - "english" - ], - "humidity": lambda wu: wu.data["hourly_forecast"][period]["humidity"], - "wind_kph": lambda wu: wu.data["hourly_forecast"][period]["wspd"][ - "metric" - ], - "wind_mph": lambda wu: wu.data["hourly_forecast"][period]["wspd"][ - "english" - ], - "pressure_mb": lambda wu: wu.data["hourly_forecast"][period]["mslp"][ - "metric" - ], - "pressure_inHg": lambda wu: wu.data["hourly_forecast"][period]["mslp"][ - "english" - ], - "date": lambda wu: wu.data["hourly_forecast"][period]["FCTTIME"][ - "pretty" - ], - }, - ) - - -class WUAlmanacSensorConfig(WUSensorConfig): - """Helper for defining field configurations for almanac sensors.""" - - def __init__( - self, - friendly_name: str | Callable, - field: str, - value_type: str, - wu_unit: str, - unit_of_measurement: str, - icon: str, - device_class=None, - ) -> None: - """Initialize almanac sensor configuration. - - :param friendly_name: Friendly name - :param field: value name returned in 'almanac' dict as returned by the WU API - :param value_type: "record" or "normal" - :param wu_unit: unit name in WU API - :param unit_of_measurement: unit of measurement - :param icon: icon name or URL - """ - super().__init__( - friendly_name=friendly_name, - feature="almanac", - value=lambda wu: wu.data["almanac"][field][value_type][wu_unit], - unit_of_measurement=unit_of_measurement, - icon=icon, - device_class="temperature", - ) - - -class WUAlertsSensorConfig(WUSensorConfig): - """Helper for defining field configuration for alerts.""" - - def __init__(self, friendly_name: str | Callable) -> None: - """Initialiize alerts sensor configuration. - - :param friendly_name: Friendly name - """ - super().__init__( - friendly_name=friendly_name, - feature="alerts", - value=lambda wu: len(wu.data["alerts"]), - icon=lambda wu: "mdi:alert-circle-outline" - if wu.data["alerts"] - else "mdi:check-circle-outline", - extra_state_attributes=self._get_attributes, - ) - - @staticmethod - def _get_attributes(rest): - - attrs = {} - - if "alerts" not in rest.data: - return attrs - - alerts = rest.data["alerts"] - multiple_alerts = len(alerts) > 1 - for data in alerts: - for alert in ALERTS_ATTRS: - if data[alert]: - if multiple_alerts: - dkey = f"{alert.capitalize()}_{data['type']}" - else: - dkey = alert.capitalize() - attrs[dkey] = data[alert] - return attrs - - -# Declaration of supported WU sensors -# (see above helper classes for argument explanation) - -SENSOR_TYPES = { - "alerts": WUAlertsSensorConfig("Alerts"), - "dewpoint_c": WUCurrentConditionsSensorConfig( - "Dewpoint", "dewpoint_c", "mdi:water", TEMP_CELSIUS - ), - "dewpoint_f": WUCurrentConditionsSensorConfig( - "Dewpoint", "dewpoint_f", "mdi:water", TEMP_FAHRENHEIT - ), - "dewpoint_string": WUCurrentConditionsSensorConfig( - "Dewpoint Summary", "dewpoint_string", "mdi:water" - ), - "feelslike_c": WUCurrentConditionsSensorConfig( - "Feels Like", "feelslike_c", "mdi:thermometer", TEMP_CELSIUS - ), - "feelslike_f": WUCurrentConditionsSensorConfig( - "Feels Like", "feelslike_f", "mdi:thermometer", TEMP_FAHRENHEIT - ), - "feelslike_string": WUCurrentConditionsSensorConfig( - "Feels Like", "feelslike_string", "mdi:thermometer" - ), - "heat_index_c": WUCurrentConditionsSensorConfig( - "Heat index", "heat_index_c", "mdi:thermometer", TEMP_CELSIUS - ), - "heat_index_f": WUCurrentConditionsSensorConfig( - "Heat index", "heat_index_f", "mdi:thermometer", TEMP_FAHRENHEIT - ), - "heat_index_string": WUCurrentConditionsSensorConfig( - "Heat Index Summary", "heat_index_string", "mdi:thermometer" - ), - "elevation": WUSensorConfig( - "Elevation", - "conditions", - value=lambda wu: wu.data["current_observation"]["observation_location"][ - "elevation" - ].split()[0], - unit_of_measurement=LENGTH_FEET, - icon="mdi:elevation-rise", - ), - "location": WUSensorConfig( - "Location", - "conditions", - value=lambda wu: wu.data["current_observation"]["display_location"]["full"], - icon="mdi:map-marker", - ), - "observation_time": WUCurrentConditionsSensorConfig( - "Observation Time", "observation_time", "mdi:clock" - ), - "precip_1hr_in": WUCurrentConditionsSensorConfig( - "Precipitation 1hr", "precip_1hr_in", "mdi:umbrella", LENGTH_INCHES - ), - "precip_1hr_metric": WUCurrentConditionsSensorConfig( - "Precipitation 1hr", "precip_1hr_metric", "mdi:umbrella", LENGTH_MILLIMETERS - ), - "precip_1hr_string": WUCurrentConditionsSensorConfig( - "Precipitation 1hr", "precip_1hr_string", "mdi:umbrella" - ), - "precip_today_in": WUCurrentConditionsSensorConfig( - "Precipitation Today", "precip_today_in", "mdi:umbrella", LENGTH_INCHES - ), - "precip_today_metric": WUCurrentConditionsSensorConfig( - "Precipitation Today", "precip_today_metric", "mdi:umbrella", LENGTH_MILLIMETERS - ), - "precip_today_string": WUCurrentConditionsSensorConfig( - "Precipitation Today", "precip_today_string", "mdi:umbrella" - ), - "pressure_in": WUCurrentConditionsSensorConfig( - "Pressure", "pressure_in", "mdi:gauge", PRESSURE_INHG, device_class="pressure" - ), - "pressure_mb": WUCurrentConditionsSensorConfig( - "Pressure", "pressure_mb", "mdi:gauge", "mb", device_class="pressure" - ), - "pressure_trend": WUCurrentConditionsSensorConfig( - "Pressure Trend", "pressure_trend", "mdi:gauge", device_class="pressure" - ), - "relative_humidity": WUSensorConfig( - "Relative Humidity", - "conditions", - value=lambda wu: int(wu.data["current_observation"]["relative_humidity"][:-1]), - unit_of_measurement=PERCENTAGE, - icon="mdi:water-percent", - device_class="humidity", - ), - "station_id": WUCurrentConditionsSensorConfig( - "Station ID", "station_id", "mdi:home" - ), - "solarradiation": WUCurrentConditionsSensorConfig( - "Solar Radiation", - "solarradiation", - "mdi:weather-sunny", - IRRADIATION_WATTS_PER_SQUARE_METER, - ), - "temperature_string": WUCurrentConditionsSensorConfig( - "Temperature Summary", "temperature_string", "mdi:thermometer" - ), - "temp_c": WUCurrentConditionsSensorConfig( - "Temperature", - "temp_c", - "mdi:thermometer", - TEMP_CELSIUS, - device_class="temperature", - ), - "temp_f": WUCurrentConditionsSensorConfig( - "Temperature", - "temp_f", - "mdi:thermometer", - TEMP_FAHRENHEIT, - device_class="temperature", - ), - "UV": WUCurrentConditionsSensorConfig("UV", "UV", "mdi:sunglasses"), - "visibility_km": WUCurrentConditionsSensorConfig( - "Visibility (km)", "visibility_km", "mdi:eye", LENGTH_KILOMETERS - ), - "visibility_mi": WUCurrentConditionsSensorConfig( - "Visibility (miles)", "visibility_mi", "mdi:eye", LENGTH_MILES - ), - "weather": WUCurrentConditionsSensorConfig("Weather Summary", "weather", None), - "wind_degrees": WUCurrentConditionsSensorConfig( - "Wind Degrees", "wind_degrees", "mdi:weather-windy", DEGREE - ), - "wind_dir": WUCurrentConditionsSensorConfig( - "Wind Direction", "wind_dir", "mdi:weather-windy" - ), - "wind_gust_kph": WUCurrentConditionsSensorConfig( - "Wind Gust", "wind_gust_kph", "mdi:weather-windy", SPEED_KILOMETERS_PER_HOUR - ), - "wind_gust_mph": WUCurrentConditionsSensorConfig( - "Wind Gust", "wind_gust_mph", "mdi:weather-windy", SPEED_MILES_PER_HOUR - ), - "wind_kph": WUCurrentConditionsSensorConfig( - "Wind Speed", "wind_kph", "mdi:weather-windy", SPEED_KILOMETERS_PER_HOUR - ), - "wind_mph": WUCurrentConditionsSensorConfig( - "Wind Speed", "wind_mph", "mdi:weather-windy", SPEED_MILES_PER_HOUR - ), - "wind_string": WUCurrentConditionsSensorConfig( - "Wind Summary", "wind_string", "mdi:weather-windy" - ), - "temp_high_record_c": WUAlmanacSensorConfig( - lambda wu: ( - f"High Temperature Record " - f"({wu.data['almanac']['temp_high']['recordyear']})" - ), - "temp_high", - "record", - "C", - TEMP_CELSIUS, - "mdi:thermometer", - ), - "temp_high_record_f": WUAlmanacSensorConfig( - lambda wu: ( - f"High Temperature Record " - f"({wu.data['almanac']['temp_high']['recordyear']})" - ), - "temp_high", - "record", - "F", - TEMP_FAHRENHEIT, - "mdi:thermometer", - ), - "temp_low_record_c": WUAlmanacSensorConfig( - lambda wu: ( - f"Low Temperature Record " - f"({wu.data['almanac']['temp_low']['recordyear']})" - ), - "temp_low", - "record", - "C", - TEMP_CELSIUS, - "mdi:thermometer", - ), - "temp_low_record_f": WUAlmanacSensorConfig( - lambda wu: ( - f"Low Temperature Record " - f"({wu.data['almanac']['temp_low']['recordyear']})" - ), - "temp_low", - "record", - "F", - TEMP_FAHRENHEIT, - "mdi:thermometer", - ), - "temp_low_avg_c": WUAlmanacSensorConfig( - "Historic Average of Low Temperatures for Today", - "temp_low", - "normal", - "C", - TEMP_CELSIUS, - "mdi:thermometer", - ), - "temp_low_avg_f": WUAlmanacSensorConfig( - "Historic Average of Low Temperatures for Today", - "temp_low", - "normal", - "F", - TEMP_FAHRENHEIT, - "mdi:thermometer", - ), - "temp_high_avg_c": WUAlmanacSensorConfig( - "Historic Average of High Temperatures for Today", - "temp_high", - "normal", - "C", - TEMP_CELSIUS, - "mdi:thermometer", - ), - "temp_high_avg_f": WUAlmanacSensorConfig( - "Historic Average of High Temperatures for Today", - "temp_high", - "normal", - "F", - TEMP_FAHRENHEIT, - "mdi:thermometer", - ), - "weather_1d": WUDailyTextForecastSensorConfig(0, "fcttext"), - "weather_1d_metric": WUDailyTextForecastSensorConfig(0, "fcttext_metric"), - "weather_1n": WUDailyTextForecastSensorConfig(1, "fcttext"), - "weather_1n_metric": WUDailyTextForecastSensorConfig(1, "fcttext_metric"), - "weather_2d": WUDailyTextForecastSensorConfig(2, "fcttext"), - "weather_2d_metric": WUDailyTextForecastSensorConfig(2, "fcttext_metric"), - "weather_2n": WUDailyTextForecastSensorConfig(3, "fcttext"), - "weather_2n_metric": WUDailyTextForecastSensorConfig(3, "fcttext_metric"), - "weather_3d": WUDailyTextForecastSensorConfig(4, "fcttext"), - "weather_3d_metric": WUDailyTextForecastSensorConfig(4, "fcttext_metric"), - "weather_3n": WUDailyTextForecastSensorConfig(5, "fcttext"), - "weather_3n_metric": WUDailyTextForecastSensorConfig(5, "fcttext_metric"), - "weather_4d": WUDailyTextForecastSensorConfig(6, "fcttext"), - "weather_4d_metric": WUDailyTextForecastSensorConfig(6, "fcttext_metric"), - "weather_4n": WUDailyTextForecastSensorConfig(7, "fcttext"), - "weather_4n_metric": WUDailyTextForecastSensorConfig(7, "fcttext_metric"), - "weather_1h": WUHourlyForecastSensorConfig(0, "condition"), - "weather_2h": WUHourlyForecastSensorConfig(1, "condition"), - "weather_3h": WUHourlyForecastSensorConfig(2, "condition"), - "weather_4h": WUHourlyForecastSensorConfig(3, "condition"), - "weather_5h": WUHourlyForecastSensorConfig(4, "condition"), - "weather_6h": WUHourlyForecastSensorConfig(5, "condition"), - "weather_7h": WUHourlyForecastSensorConfig(6, "condition"), - "weather_8h": WUHourlyForecastSensorConfig(7, "condition"), - "weather_9h": WUHourlyForecastSensorConfig(8, "condition"), - "weather_10h": WUHourlyForecastSensorConfig(9, "condition"), - "weather_11h": WUHourlyForecastSensorConfig(10, "condition"), - "weather_12h": WUHourlyForecastSensorConfig(11, "condition"), - "weather_13h": WUHourlyForecastSensorConfig(12, "condition"), - "weather_14h": WUHourlyForecastSensorConfig(13, "condition"), - "weather_15h": WUHourlyForecastSensorConfig(14, "condition"), - "weather_16h": WUHourlyForecastSensorConfig(15, "condition"), - "weather_17h": WUHourlyForecastSensorConfig(16, "condition"), - "weather_18h": WUHourlyForecastSensorConfig(17, "condition"), - "weather_19h": WUHourlyForecastSensorConfig(18, "condition"), - "weather_20h": WUHourlyForecastSensorConfig(19, "condition"), - "weather_21h": WUHourlyForecastSensorConfig(20, "condition"), - "weather_22h": WUHourlyForecastSensorConfig(21, "condition"), - "weather_23h": WUHourlyForecastSensorConfig(22, "condition"), - "weather_24h": WUHourlyForecastSensorConfig(23, "condition"), - "weather_25h": WUHourlyForecastSensorConfig(24, "condition"), - "weather_26h": WUHourlyForecastSensorConfig(25, "condition"), - "weather_27h": WUHourlyForecastSensorConfig(26, "condition"), - "weather_28h": WUHourlyForecastSensorConfig(27, "condition"), - "weather_29h": WUHourlyForecastSensorConfig(28, "condition"), - "weather_30h": WUHourlyForecastSensorConfig(29, "condition"), - "weather_31h": WUHourlyForecastSensorConfig(30, "condition"), - "weather_32h": WUHourlyForecastSensorConfig(31, "condition"), - "weather_33h": WUHourlyForecastSensorConfig(32, "condition"), - "weather_34h": WUHourlyForecastSensorConfig(33, "condition"), - "weather_35h": WUHourlyForecastSensorConfig(34, "condition"), - "weather_36h": WUHourlyForecastSensorConfig(35, "condition"), - "temp_high_1d_c": WUDailySimpleForecastSensorConfig( - "High Temperature Today", - 0, - "high", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_2d_c": WUDailySimpleForecastSensorConfig( - "High Temperature Tomorrow", - 1, - "high", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_3d_c": WUDailySimpleForecastSensorConfig( - "High Temperature in 3 Days", - 2, - "high", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_4d_c": WUDailySimpleForecastSensorConfig( - "High Temperature in 4 Days", - 3, - "high", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_1d_f": WUDailySimpleForecastSensorConfig( - "High Temperature Today", - 0, - "high", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_2d_f": WUDailySimpleForecastSensorConfig( - "High Temperature Tomorrow", - 1, - "high", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_3d_f": WUDailySimpleForecastSensorConfig( - "High Temperature in 3 Days", - 2, - "high", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_high_4d_f": WUDailySimpleForecastSensorConfig( - "High Temperature in 4 Days", - 3, - "high", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_1d_c": WUDailySimpleForecastSensorConfig( - "Low Temperature Today", - 0, - "low", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_2d_c": WUDailySimpleForecastSensorConfig( - "Low Temperature Tomorrow", - 1, - "low", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_3d_c": WUDailySimpleForecastSensorConfig( - "Low Temperature in 3 Days", - 2, - "low", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_4d_c": WUDailySimpleForecastSensorConfig( - "Low Temperature in 4 Days", - 3, - "low", - "celsius", - TEMP_CELSIUS, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_1d_f": WUDailySimpleForecastSensorConfig( - "Low Temperature Today", - 0, - "low", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_2d_f": WUDailySimpleForecastSensorConfig( - "Low Temperature Tomorrow", - 1, - "low", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_3d_f": WUDailySimpleForecastSensorConfig( - "Low Temperature in 3 Days", - 2, - "low", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "temp_low_4d_f": WUDailySimpleForecastSensorConfig( - "Low Temperature in 4 Days", - 3, - "low", - "fahrenheit", - TEMP_FAHRENHEIT, - "mdi:thermometer", - device_class="temperature", - ), - "wind_gust_1d_kph": WUDailySimpleForecastSensorConfig( - "Max. Wind Today", - 0, - "maxwind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_2d_kph": WUDailySimpleForecastSensorConfig( - "Max. Wind Tomorrow", - 1, - "maxwind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_3d_kph": WUDailySimpleForecastSensorConfig( - "Max. Wind in 3 Days", - 2, - "maxwind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_4d_kph": WUDailySimpleForecastSensorConfig( - "Max. Wind in 4 Days", - 3, - "maxwind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_1d_mph": WUDailySimpleForecastSensorConfig( - "Max. Wind Today", - 0, - "maxwind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_2d_mph": WUDailySimpleForecastSensorConfig( - "Max. Wind Tomorrow", - 1, - "maxwind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_3d_mph": WUDailySimpleForecastSensorConfig( - "Max. Wind in 3 Days", - 2, - "maxwind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_gust_4d_mph": WUDailySimpleForecastSensorConfig( - "Max. Wind in 4 Days", - 3, - "maxwind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_1d_kph": WUDailySimpleForecastSensorConfig( - "Avg. Wind Today", - 0, - "avewind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_2d_kph": WUDailySimpleForecastSensorConfig( - "Avg. Wind Tomorrow", - 1, - "avewind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_3d_kph": WUDailySimpleForecastSensorConfig( - "Avg. Wind in 3 Days", - 2, - "avewind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_4d_kph": WUDailySimpleForecastSensorConfig( - "Avg. Wind in 4 Days", - 3, - "avewind", - SPEED_KILOMETERS_PER_HOUR, - SPEED_KILOMETERS_PER_HOUR, - "mdi:weather-windy", - ), - "wind_1d_mph": WUDailySimpleForecastSensorConfig( - "Avg. Wind Today", - 0, - "avewind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_2d_mph": WUDailySimpleForecastSensorConfig( - "Avg. Wind Tomorrow", - 1, - "avewind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_3d_mph": WUDailySimpleForecastSensorConfig( - "Avg. Wind in 3 Days", - 2, - "avewind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "wind_4d_mph": WUDailySimpleForecastSensorConfig( - "Avg. Wind in 4 Days", - 3, - "avewind", - SPEED_MILES_PER_HOUR, - SPEED_MILES_PER_HOUR, - "mdi:weather-windy", - ), - "precip_1d_mm": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity Today", - 0, - "qpf_allday", - LENGTH_MILLIMETERS, - LENGTH_MILLIMETERS, - "mdi:umbrella", - ), - "precip_2d_mm": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity Tomorrow", - 1, - "qpf_allday", - LENGTH_MILLIMETERS, - LENGTH_MILLIMETERS, - "mdi:umbrella", - ), - "precip_3d_mm": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity in 3 Days", - 2, - "qpf_allday", - LENGTH_MILLIMETERS, - LENGTH_MILLIMETERS, - "mdi:umbrella", - ), - "precip_4d_mm": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity in 4 Days", - 3, - "qpf_allday", - LENGTH_MILLIMETERS, - LENGTH_MILLIMETERS, - "mdi:umbrella", - ), - "precip_1d_in": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity Today", - 0, - "qpf_allday", - "in", - LENGTH_INCHES, - "mdi:umbrella", - ), - "precip_2d_in": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity Tomorrow", - 1, - "qpf_allday", - "in", - LENGTH_INCHES, - "mdi:umbrella", - ), - "precip_3d_in": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity in 3 Days", - 2, - "qpf_allday", - "in", - LENGTH_INCHES, - "mdi:umbrella", - ), - "precip_4d_in": WUDailySimpleForecastSensorConfig( - "Precipitation Intensity in 4 Days", - 3, - "qpf_allday", - "in", - LENGTH_INCHES, - "mdi:umbrella", - ), - "precip_1d": WUDailySimpleForecastSensorConfig( - "Precipitation Probability Today", - 0, - "pop", - None, - PERCENTAGE, - "mdi:umbrella", - ), - "precip_2d": WUDailySimpleForecastSensorConfig( - "Precipitation Probability Tomorrow", - 1, - "pop", - None, - PERCENTAGE, - "mdi:umbrella", - ), - "precip_3d": WUDailySimpleForecastSensorConfig( - "Precipitation Probability in 3 Days", - 2, - "pop", - None, - PERCENTAGE, - "mdi:umbrella", - ), - "precip_4d": WUDailySimpleForecastSensorConfig( - "Precipitation Probability in 4 Days", - 3, - "pop", - None, - PERCENTAGE, - "mdi:umbrella", - ), -} - -# Alert Attributes -ALERTS_ATTRS = ["date", "description", "expires", "message"] - -# Language Supported Codes -LANG_CODES = [ - "AF", - "AL", - "AR", - "HY", - "AZ", - "EU", - "BY", - "BU", - "LI", - "MY", - "CA", - "CN", - "TW", - "CR", - "CZ", - "DK", - "DV", - "NL", - "EN", - "EO", - "ET", - "FA", - "FI", - "FR", - "FC", - "GZ", - "DL", - "KA", - "GR", - "GU", - "HT", - "IL", - "HI", - "HU", - "IS", - "IO", - "ID", - "IR", - "IT", - "JP", - "JW", - "KM", - "KR", - "KU", - "LA", - "LV", - "LT", - "ND", - "MK", - "MT", - "GM", - "MI", - "MR", - "MN", - "NO", - "OC", - "PS", - "GN", - "PL", - "BR", - "PA", - "RO", - "RU", - "SR", - "SK", - "SL", - "SP", - "SI", - "SW", - "CH", - "TL", - "TT", - "TH", - "TR", - "TK", - "UA", - "UZ", - "VU", - "CY", - "SN", - "JI", - "YI", -] - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Optional(CONF_PWS_ID): cv.string, - vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.All(vol.In(LANG_CODES)), - vol.Inclusive( - CONF_LATITUDE, "coordinates", "Latitude and longitude must exist together" - ): cv.latitude, - vol.Inclusive( - CONF_LONGITUDE, "coordinates", "Latitude and longitude must exist together" - ): cv.longitude, - vol.Required(CONF_MONITORED_CONDITIONS): vol.All( - cv.ensure_list, vol.Length(min=1), [vol.In(SENSOR_TYPES)] - ), - } -) - - -async def async_setup_platform( - hass: HomeAssistant, config: ConfigType, async_add_entities, discovery_info=None -): - """Set up the WUnderground sensor.""" - latitude = config.get(CONF_LATITUDE, hass.config.latitude) - longitude = config.get(CONF_LONGITUDE, hass.config.longitude) - pws_id = config.get(CONF_PWS_ID) - - rest = WUndergroundData( - hass, - config.get(CONF_API_KEY), - pws_id, - config.get(CONF_LANG), - latitude, - longitude, - ) - - if pws_id is None: - unique_id_base = f"@{longitude:06f},{latitude:06f}" - else: - # Manually specified weather station, use that for unique_id - unique_id_base = pws_id - sensors = [] - for variable in config[CONF_MONITORED_CONDITIONS]: - sensors.append(WUndergroundSensor(hass, rest, variable, unique_id_base)) - - await rest.async_update() - if not rest.data: - raise PlatformNotReady - - async_add_entities(sensors, True) - - -class WUndergroundSensor(SensorEntity): - """Implementing the WUnderground sensor.""" - - def __init__( - self, hass: HomeAssistant, rest, condition, unique_id_base: str - ) -> None: - """Initialize the sensor.""" - self.rest = rest - self._condition = condition - self._state = None - self._attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} - self._icon = None - self._entity_picture = None - self._unit_of_measurement = self._cfg_expand("unit_of_measurement") - self.rest.request_feature(SENSOR_TYPES[condition].feature) - # This is only the suggested entity id, it might get changed by - # the entity registry later. - self.entity_id = sensor.ENTITY_ID_FORMAT.format(f"pws_{condition}") - self._unique_id = f"{unique_id_base},{condition}" - self._device_class = self._cfg_expand("device_class") - - def _cfg_expand(self, what, default=None): - """Parse and return sensor data.""" - cfg = SENSOR_TYPES[self._condition] - val = getattr(cfg, what) - if not callable(val): - return val - try: - val = val(self.rest) - except (KeyError, IndexError, TypeError, ValueError) as err: - _LOGGER.warning( - "Failed to expand cfg from WU API. Condition: %s Attr: %s Error: %s", - self._condition, - what, - repr(err), - ) - val = default - - return val - - def _update_attrs(self): - """Parse and update device state attributes.""" - attrs = self._cfg_expand("extra_state_attributes", {}) - - for (attr, callback) in attrs.items(): - if callable(callback): - try: - self._attributes[attr] = callback(self.rest) - except (KeyError, IndexError, TypeError, ValueError) as err: - _LOGGER.warning( - "Failed to update attrs from WU API." - " Condition: %s Attr: %s Error: %s", - self._condition, - attr, - repr(err), - ) - else: - self._attributes[attr] = callback - - @property - def name(self): - """Return the name of the sensor.""" - return self._cfg_expand("friendly_name") - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return self._attributes - - @property - def icon(self): - """Return icon.""" - return self._icon - - @property - def entity_picture(self): - """Return the entity picture.""" - return self._entity_picture - - @property - def unit_of_measurement(self): - """Return the units of measurement.""" - return self._unit_of_measurement - - @property - def device_class(self): - """Return the units of measurement.""" - return self._device_class - - async def async_update(self): - """Update current conditions.""" - await self.rest.async_update() - - if not self.rest.data: - # no data, return - return - - self._state = self._cfg_expand("value") - self._update_attrs() - self._icon = self._cfg_expand("icon", super().icon) - url = self._cfg_expand("entity_picture") - if isinstance(url, str): - self._entity_picture = re.sub( - r"^http://", "https://", url, flags=re.IGNORECASE - ) - - @property - def unique_id(self) -> str: - """Return a unique ID.""" - return self._unique_id - - -class WUndergroundData: - """Get data from WUnderground.""" - - def __init__(self, hass, api_key, pws_id, lang, latitude, longitude): - """Initialize the data object.""" - self._hass = hass - self._api_key = api_key - self._pws_id = pws_id - self._lang = f"lang:{lang}" - self._latitude = latitude - self._longitude = longitude - self._features = set() - self.data = None - self._session = async_get_clientsession(self._hass) - - def request_feature(self, feature): - """Register feature to be fetched from WU API.""" - self._features.add(feature) - - def _build_url(self, baseurl=_RESOURCE): - url = baseurl.format( - self._api_key, "/".join(sorted(self._features)), self._lang - ) - if self._pws_id: - url = f"{url}pws:{self._pws_id}" - else: - url = f"{url}{self._latitude},{self._longitude}" - - return f"{url}.json" - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - async def async_update(self): - """Get the latest data from WUnderground.""" - try: - with async_timeout.timeout(10): - response = await self._session.get(self._build_url()) - result = await response.json() - if "error" in result["response"]: - raise ValueError(result["response"]["error"]["description"]) - self.data = result - except ValueError as err: - _LOGGER.error("Check WUnderground API %s", err.args) - except (asyncio.TimeoutError, aiohttp.ClientError) as err: - _LOGGER.error("Error fetching WUnderground data: %s", repr(err)) diff --git a/mypy.ini b/mypy.ini index 03fc0781027..1479bb7c700 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1626,9 +1626,6 @@ ignore_errors = true [mypy-homeassistant.components.withings.*] ignore_errors = true -[mypy-homeassistant.components.wunderground.*] -ignore_errors = true - [mypy-homeassistant.components.xbox.*] ignore_errors = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index d28f56bdea4..013e52c0327 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -204,7 +204,6 @@ IGNORED_MODULES: Final[list[str]] = [ "homeassistant.components.wemo.*", "homeassistant.components.wink.*", "homeassistant.components.withings.*", - "homeassistant.components.wunderground.*", "homeassistant.components.xbox.*", "homeassistant.components.xiaomi_aqara.*", "homeassistant.components.xiaomi_miio.*", diff --git a/tests/components/wunderground/__init__.py b/tests/components/wunderground/__init__.py deleted file mode 100644 index d3f839a35f6..00000000000 --- a/tests/components/wunderground/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the wunderground component.""" diff --git a/tests/components/wunderground/test_sensor.py b/tests/components/wunderground/test_sensor.py deleted file mode 100644 index 8709f5b6a46..00000000000 --- a/tests/components/wunderground/test_sensor.py +++ /dev/null @@ -1,188 +0,0 @@ -"""The tests for the WUnderground platform.""" -import aiohttp -from pytest import raises - -import homeassistant.components.wunderground.sensor as wunderground -from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, - LENGTH_INCHES, - STATE_UNKNOWN, - TEMP_CELSIUS, -) -from homeassistant.exceptions import PlatformNotReady -from homeassistant.setup import async_setup_component - -from tests.common import assert_setup_component, load_fixture - -VALID_CONFIG_PWS = { - "platform": "wunderground", - "api_key": "foo", - "pws_id": "bar", - "monitored_conditions": [ - "weather", - "feelslike_c", - "alerts", - "elevation", - "location", - ], -} - -VALID_CONFIG = { - "platform": "wunderground", - "api_key": "foo", - "lang": "EN", - "monitored_conditions": [ - "weather", - "feelslike_c", - "alerts", - "elevation", - "location", - "weather_1d_metric", - "precip_1d_in", - ], -} - -INVALID_CONFIG = { - "platform": "wunderground", - "api_key": "BOB", - "pws_id": "bar", - "lang": "foo", - "monitored_conditions": ["weather", "feelslike_c", "alerts"], -} - -URL = ( - "http://api.wunderground.com/api/foo/alerts/conditions/forecast/lang" - ":EN/q/32.87336,-117.22743.json" -) -PWS_URL = "http://api.wunderground.com/api/foo/alerts/conditions/lang:EN/q/pws:bar.json" -INVALID_URL = ( - "http://api.wunderground.com/api/BOB/alerts/conditions/lang:foo/q/pws:bar.json" -) - - -async def test_setup(hass, aioclient_mock): - """Test that the component is loaded.""" - aioclient_mock.get(URL, text=load_fixture("wunderground-valid.json")) - - with assert_setup_component(1, "sensor"): - await async_setup_component(hass, "sensor", {"sensor": VALID_CONFIG}) - await hass.async_block_till_done() - - -async def test_setup_pws(hass, aioclient_mock): - """Test that the component is loaded with PWS id.""" - aioclient_mock.get(PWS_URL, text=load_fixture("wunderground-valid.json")) - - with assert_setup_component(1, "sensor"): - await async_setup_component(hass, "sensor", {"sensor": VALID_CONFIG_PWS}) - - -async def test_setup_invalid(hass, aioclient_mock): - """Test that the component is not loaded with invalid config.""" - aioclient_mock.get(INVALID_URL, text=load_fixture("wunderground-error.json")) - - with assert_setup_component(0, "sensor"): - await async_setup_component(hass, "sensor", {"sensor": INVALID_CONFIG}) - - -async def test_sensor(hass, aioclient_mock): - """Test the WUnderground sensor class and methods.""" - aioclient_mock.get(URL, text=load_fixture("wunderground-valid.json")) - - await async_setup_component(hass, "sensor", {"sensor": VALID_CONFIG}) - await hass.async_block_till_done() - - state = hass.states.get("sensor.pws_weather") - assert state.state == "Clear" - assert state.name == "Weather Summary" - assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes - assert ( - state.attributes["entity_picture"] == "https://icons.wxug.com/i/c/k/clear.gif" - ) - - state = hass.states.get("sensor.pws_alerts") - assert state.state == "1" - assert state.name == "Alerts" - assert state.attributes["Message"] == "This is a test alert message" - assert state.attributes["icon"] == "mdi:alert-circle-outline" - assert "entity_picture" not in state.attributes - - state = hass.states.get("sensor.pws_location") - assert state.state == "Holly Springs, NC" - assert state.name == "Location" - - state = hass.states.get("sensor.pws_elevation") - assert state.state == "413" - assert state.name == "Elevation" - - state = hass.states.get("sensor.pws_feelslike_c") - assert state.state == "40" - assert state.name == "Feels Like" - assert "entity_picture" not in state.attributes - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS - - state = hass.states.get("sensor.pws_weather_1d_metric") - assert state.state == "Mostly Cloudy. Fog overnight." - assert state.name == "Tuesday" - - state = hass.states.get("sensor.pws_precip_1d_in") - assert state.state == "0.03" - assert state.name == "Precipitation Intensity Today" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == LENGTH_INCHES - - -async def test_connect_failed(hass, aioclient_mock): - """Test the WUnderground connection error.""" - aioclient_mock.get(URL, exc=aiohttp.ClientError()) - with raises(PlatformNotReady): - await wunderground.async_setup_platform(hass, VALID_CONFIG, lambda _: None) - - -async def test_invalid_data(hass, aioclient_mock): - """Test the WUnderground invalid data.""" - aioclient_mock.get(URL, text=load_fixture("wunderground-invalid.json")) - - await async_setup_component(hass, "sensor", {"sensor": VALID_CONFIG}) - await hass.async_block_till_done() - - for condition in VALID_CONFIG["monitored_conditions"]: - state = hass.states.get(f"sensor.pws_{condition}") - assert state.state == STATE_UNKNOWN - - -async def test_entity_id_with_multiple_stations(hass, aioclient_mock): - """Test not generating duplicate entity ids with multiple stations.""" - aioclient_mock.get(URL, text=load_fixture("wunderground-valid.json")) - aioclient_mock.get(PWS_URL, text=load_fixture("wunderground-valid.json")) - - config = [VALID_CONFIG, {**VALID_CONFIG_PWS, "entity_namespace": "hi"}] - await async_setup_component(hass, "sensor", {"sensor": config}) - await hass.async_block_till_done() - - state = hass.states.get("sensor.pws_weather") - assert state is not None - assert state.state == "Clear" - - state = hass.states.get("sensor.hi_pws_weather") - assert state is not None - assert state.state == "Clear" - - -async def test_fails_because_of_unique_id(hass, aioclient_mock): - """Test same config twice fails because of unique_id.""" - aioclient_mock.get(URL, text=load_fixture("wunderground-valid.json")) - aioclient_mock.get(PWS_URL, text=load_fixture("wunderground-valid.json")) - - config = [ - VALID_CONFIG, - {**VALID_CONFIG, "entity_namespace": "hi"}, - VALID_CONFIG_PWS, - ] - await async_setup_component(hass, "sensor", {"sensor": config}) - await hass.async_block_till_done() - - states = hass.states.async_all() - expected = len(VALID_CONFIG["monitored_conditions"]) + len( - VALID_CONFIG_PWS["monitored_conditions"] - ) - assert len(states) == expected diff --git a/tests/fixtures/wunderground-error.json b/tests/fixtures/wunderground-error.json deleted file mode 100644 index 264ecbf8cd6..00000000000 --- a/tests/fixtures/wunderground-error.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "response": { - "version": "0.1", - "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", - "features": {}, - "error": { - "type": "keynotfound", - "description": "this key does not exist" - } - } -} diff --git a/tests/fixtures/wunderground-invalid.json b/tests/fixtures/wunderground-invalid.json deleted file mode 100644 index 59661c6694d..00000000000 --- a/tests/fixtures/wunderground-invalid.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "response": { - "version": "0.1", - "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", - "features": { - "conditions": 1, - "alerts": 1, - "forecast": 1 - } - }, - "current_observation": { - "image": { - "url": "http://icons.wxug.com/graphics/wu2/logo_130x80.png", - "title": "Weather Underground", - "link": "http://www.wunderground.com" - } - } -} diff --git a/tests/fixtures/wunderground-valid.json b/tests/fixtures/wunderground-valid.json deleted file mode 100644 index 7ac1081cb4e..00000000000 --- a/tests/fixtures/wunderground-valid.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "response": { - "version": "0.1", - "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", - "features": { - "conditions": 1, - "alerts": 1, - "forecast": 1 - } - }, - "current_observation": { - "image": { - "url": "http://icons.wxug.com/graphics/wu2/logo_130x80.png", - "title": "Weather Underground", - "link": "http://www.wunderground.com" - }, - "feelslike_c": "40", - "weather": "Clear", - "icon_url": "http://icons.wxug.com/i/c/k/clear.gif", - "display_location": { - "city": "Holly Springs", - "country": "US", - "full": "Holly Springs, NC" - }, - "observation_location": { - "elevation": "413 ft", - "full": "Twin Lake, Holly Springs, North Carolina" - } - }, - "alerts": [ - { - "type": "FLO", - "description": "Areal Flood Warning", - "date": "9:36 PM CDT on September 22, 2016", - "expires": "10:00 AM CDT on September 23, 2016", - "message": "This is a test alert message" - } - ], - "forecast": { - "txt_forecast": { - "date": "22:35 CEST", - "forecastday": [ - { - "period": 0, - "icon_url": "http://icons.wxug.com/i/c/k/clear.gif", - "title": "Tuesday", - "fcttext": "Mostly Cloudy. Fog overnight.", - "fcttext_metric": "Mostly Cloudy. Fog overnight.", - "pop": "0" - } - ] - }, - "simpleforecast": { - "forecastday": [ - { - "date": { - "pretty": "19:00 CEST 4. Duben 2017" - }, - "period": 1, - "high": { - "fahrenheit": "56", - "celsius": "13" - }, - "low": { - "fahrenheit": "43", - "celsius": "6" - }, - "conditions": "Mo\u017enost de\u0161t\u011b", - "icon_url": "http://icons.wxug.com/i/c/k/chancerain.gif", - "qpf_allday": { - "in": 0.03, - "mm": 1 - }, - "maxwind": { - "mph": 0, - "kph": 0, - "dir": "", - "degrees": 0 - }, - "avewind": { - "mph": 0, - "kph": 0, - "dir": "severn\u00ed", - "degrees": 0 - } - } - ] - } - } -}